protected function doWork($socket)
    {
        Connection::send($socket, 'grab_job');

        $resp = array('function' => 'noop');
        while (count($resp) && $resp['function'] == 'noop') {
            $resp = Connection::blockingRead($socket);
        }

        if (in_array($resp['function'], array('noop', 'no_job'))) {
            return false;
        }

        if ($resp['function'] != 'job_assign') {
            throw new Exception('Internal error - Job was not assigned after it was grabbed by this worker');
        }

        $name = $resp['data']['func'];
        $handle = $resp['data']['handle'];
        $arg = array();

        if (isset($resp['data']['arg']) &&
            Connection::stringLength($resp['data']['arg'])) {
            $arg = json_decode($resp['data']['arg'], true);
            if ($arg === null) {
                $arg = $resp['data']['arg'];
            }
        }

        try {
            $this->callStartCallbacks($handle, $name, $arg);

            $functionCallback = $this->functions[$name]['callback'];
            $result = call_user_func($functionCallback, $arg);
            if (!$result) {
                $result = '';
            }

            $this->jobComplete($socket, $handle, $result);
            $this->callCompleteCallbacks($handle, $name, $result);
        } catch (JobException $e) {
            $this->jobFail($socket, $handle);
            $this->callFailCallbacks($handle, $name, $e);
        }

        // Force the job's destructor to run
        $job = null;

        return true;
    }