Example #1
0
void Process::finish(int returnCode)
{
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mReturn = returnCode;

        mStdInBuffer.clear();
        closeStdIn(CloseForce);
        mWantStdInClosed = false;

        if (mMode == Async) {
            // try to read all remaining data on stdout and stderr
            handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
            handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

            closeStdOut();
            closeStdErr();
        } else {
            int w;
            char q = 'q';
            eintrwrap(w, ::write(mSync[1], &q, 1));
            eintrwrap(w, ::close(mSync[1]));
            mSync[1] = -1;
        }
    }

    if (mMode == Async)
        mFinished(this);
}
void Process::event(const Event* event)
{
    if (event->type() == ProcessFinishedEvent::Type) {
        const ProcessFinishedEvent* pevent = static_cast<const ProcessFinishedEvent*>(event);
        if (mPid == -1) {
            error() << "process already finished, pid " << pevent->pid;
            return;
        }
        assert(mPid == pevent->pid);
        mPid = -1;
        mReturn = pevent->returnCode;

        mStdInBuffer.clear();
        closeStdIn();

        // try to read all remaining data on stdout and stderr
        handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
        handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

        closeStdOut();
        closeStdErr();

        mFinished();
    } else {
        EventReceiver::event(event);
    }
}
Example #3
0
void MPlayer::mStarted()
{
    if (play_list.size()) {
        play(play_list.at(0));
        play_list.removeAt(0);
        QTimer::singleShot(1000, this, SLOT(setDuration()));
    } else {
        emit mFinished();
    }
}
Example #4
0
void Connection::onDataAvailable(const SocketClient::SharedPtr&, Buffer&& buf)
{
    while (true) {
        if (!buf.isEmpty())
            mBuffers.push_back(std::move(buf));

        unsigned int available = bufferSize(mBuffers);
        if (!available)
            break;
        if (!mPendingRead) {
            if (available < static_cast<int>(sizeof(uint32_t)))
                break;
            char buf[sizeof(uint32_t)];
            const int read = bufferRead(mBuffers, buf, 4);
            assert(read == 4);
            Deserializer strm(buf, read);
            strm >> mPendingRead;
            assert(mPendingRead > 0);
            available -= 4;
        }
        assert(mPendingRead >= 0);
        if (available < static_cast<unsigned int>(mPendingRead))
            break;
        char buf[1024];
        char *buffer = buf;
        if (mPendingRead > static_cast<int>(sizeof(buf))) {
            buffer = new char[mPendingRead];
        }
        const int read = bufferRead(mBuffers, buffer, mPendingRead);
        assert(read == mPendingRead);
        mPendingRead = 0;
        std::shared_ptr<Message> message = Message::create(mVersion, buffer, read);
        if (message) {
            auto that = shared_from_this();
            if (message->messageId() == FinishMessage::MessageId) {
                mFinishStatus = std::static_pointer_cast<FinishMessage>(message)->status();
                mFinished(that, mFinishStatus);
            } else if (message->messageId() == ConnectMessage::MessageId) {
                mIsConnected = true;
            } else {
                newMessage()(message, that);
            }
        }
        if (buffer != buf)
            delete[] buffer;
        if (!message)
            mSocketClient->close();
    // mClient->dataAvailable().disconnect(this, &Connection::dataAvailable);
    }
}
Example #5
0
void Connection::onDataAvailable(const SocketClient::SharedPtr &client, Buffer&& buf)
{
    auto that = shared_from_this();
    while (true) {
        if (!buf.isEmpty())
            mBuffers.push(std::forward<Buffer>(buf));

        unsigned int available = mBuffers.size();
        if (!available)
            break;
        if (!mPendingRead) {
            if (available < static_cast<int>(sizeof(uint32_t)))
                break;
            union {
                unsigned char b[sizeof(uint32_t)];
                int pending;
            };
            const int read = mBuffers.read(b, 4);
            assert(read == 4);
            mPendingRead = pending;
            assert(mPendingRead > 0);
            available -= read;
        }
        assert(mPendingRead >= 0);
        if (available < static_cast<unsigned int>(mPendingRead))
            break;

        StackBuffer<1024 * 16> buffer(mPendingRead);
        const int read = mBuffers.read(buffer.buffer(), mPendingRead);
        assert(read == mPendingRead);
        mPendingRead = 0;
        Message::MessageError error;
        std::shared_ptr<Message> message = Message::create(mVersion, buffer, read, &error);
        if (message) {
            if (message->messageId() == FinishMessage::MessageId) {
                mFinishStatus = std::static_pointer_cast<FinishMessage>(message)->status();
                mFinished(that, mFinishStatus);
            } else {
                newMessage()(message, that);
            }
        } else if (mErrorHandler) {
            mErrorHandler(client, std::move(error));
        } else {
            ::error() << "Unable to create message from data" << error.type << error.text << read;
        }
        if (!message)
            client->close();
    }
}
Example #6
0
void Connection::onDataAvailable(const SocketClient::SharedPtr&, Buffer&& buf)
{
    while (true) {
        if (!buf.isEmpty())
            mBuffers.push_back(std::move(buf));

        unsigned int available = bufferSize(mBuffers);
        if (!available)
            break;
        if (!mPendingRead) {
            if (available < static_cast<int>(sizeof(uint32_t)))
                break;
            union {
                char buf[sizeof(uint32_t)];
                int pending;
            };
            const int read = bufferRead(mBuffers, buf, 4);
            assert(read == 4);
            mPendingRead = pending;
            assert(mPendingRead > 0);
            available -= read;
        }
        assert(mPendingRead >= 0);
        if (available < static_cast<unsigned int>(mPendingRead))
            break;

        StackBuffer<1024 * 16> buffer(mPendingRead);
        const int read = bufferRead(mBuffers, buffer, mPendingRead);
        assert(read == mPendingRead);
        mPendingRead = 0;
        std::shared_ptr<Message> message = Message::create(mVersion, buffer, read);
        if (message) {
            auto that = shared_from_this();
            if (message->messageId() == FinishMessage::MessageId) {
                mFinishStatus = std::static_pointer_cast<FinishMessage>(message)->status();
                mFinished(that, mFinishStatus);
            } else {
                newMessage()(message, that);
            }
        } else {
            ::error() << "Unable to create message from data" << read;
        }
        if (!message)
            mSocketClient->close();
    // mClient->dataAvailable().disconnect(this, &Connection::dataAvailable);
    }
}
Example #7
0
void ScanThread::run()
{
    mFinished(paths(mPath, mFilters));
}
void ScanJob::run()
{
    mPath.visit(&ScanJob::visit, this);
    if (shared_ptr<Project> project = mProject.lock())
        mFinished(mPaths);
}
Example #9
0
void CompletionJob::execute()
{
    StopWatch timer;
    CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(),
                                  mUnsaved.isEmpty() ? 0 : mUnsaved.constData(),
                                  static_cast<unsigned long>(mUnsaved.size()) };
    if (!mUnit) {
        String clangLine;
        RTags::parseTranslationUnit(mPath, mArgs, mUnit, Server::instance()->clangIndex(), clangLine,
                                    0, 0, &unsavedFile, 1);
        mParseCount = 1;
        if (!mUnit) {
            error() << "Failed to parse" << mPath << "Can't complete";
            return;
        }
    }
    // error() << "Completing" << mPath << mParseCount;
    assert(mParseCount >= 1 && mParseCount <= 2);
    if (mParseCount == 1) {
        RTags::reparseTranslationUnit(mUnit, &unsavedFile, 1);
        if (!mUnit) {
            mFinished(mPath, id());
            return;
        } else {
            ++mParseCount;
        }
    }

    CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn,
                                                          &unsavedFile, mUnsaved.isEmpty() ? 0 : 1,
                                                          CXCodeComplete_IncludeMacros
                                                          | CXCodeComplete_IncludeCodePatterns);
    if (results) {
        CompletionNode *nodes = new CompletionNode[results->NumResults];
        int nodeCount = 0;
        Map<Token, int> tokens;
        if (!mUnsaved.isEmpty()) {
            tokenize(mUnsaved.constData(), mUnsaved.size(), tokens);
            // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
            //     error() << String(it->first.data, it->first.length) << it->second;
            // }
        }
        for (unsigned i = 0; i < results->NumResults; ++i) {
            const CXCursorKind kind = results->Results[i].CursorKind;
            if (kind == CXCursor_Destructor)
                continue;

            const CXCompletionString &string = results->Results[i].CompletionString;
            const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string);
            if (availabilityKind != CXAvailability_Available)
                continue;

            const int priority = clang_getCompletionPriority(string);
            if (priority >= 75)
                continue;

            CompletionNode &node = nodes[nodeCount];
            node.priority = priority;
            node.signature.reserve(256);
            const int chunkCount = clang_getNumCompletionChunks(string);
            bool ok = true;
            for (int j=0; j<chunkCount; ++j) {
                const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j);
                if (chunkKind == CXCompletionChunk_TypedText) {
                    node.completion = RTags::eatString(clang_getCompletionChunkText(string, j));
                    if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) {
                        ok = false;
                        break;
                    }
                    node.signature.append(node.completion);
                } else {
                    node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j)));
                    if (chunkKind == CXCompletionChunk_ResultType)
                        node.signature.append(' ');
                }
            }

            if (ok) {
                int ws = node.completion.size() - 1;
                while (ws >= 0 && isspace(node.completion.at(ws)))
                    --ws;
                if (ws >= 0) {
                    node.completion.truncate(ws + 1);
                    node.signature.replace("\n", "");
                    node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1);
                    ++nodeCount;
                    continue;
                }
            }
            node.completion.clear();
            node.signature.clear();
        }
        if (nodeCount) {
            if (nodeCount > SendThreshold) {
                write("`");
            } else {
                qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode);
                if (mType == Stream) {
                    write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData());
                } else {
                    write<128>("%s %s", nodes[0].completion.constData(), nodes[0].signature.constData());
                }
                for (int i=1; i<nodeCount; ++i) {
                    write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData());
                }
            }
        }

        warning() << "Wrote" << ((nodeCount > SendThreshold) ? -1 : nodeCount) << "completions for"
                  << String::format<128>("%s:%d:%d", mPath.constData(), mLine, mColumn)
                  << "in" << timer.elapsed() << "ms" << mArgs;
        // const unsigned diagnosticCount = clang_getNumDiagnostics(mUnit);
        // for (unsigned i=0; i<diagnosticCount; ++i) {
        //     CXDiagnostic diagnostic = clang_getDiagnostic(mUnit, i);
        //     const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diagnostic);
        //     const String msg = RTags::eatString(clang_getDiagnosticSpelling(diagnostic));
        //     CXFile file;
        //     unsigned line, col;
        //     clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file, &line, &col, 0);

        //     error() << i << diagnosticCount << severity << msg
        //             << String::format<128>("%s:%d:%d", RTags::eatString(clang_getFileName(file)).constData(),
        //                                    line, col);
        //     clang_disposeDiagnostic(diagnostic);
        // }

        delete[] nodes;

        //processDiagnostics(results);

        clang_disposeCodeCompleteResults(results);
        std::shared_ptr<Project> proj = project();
        if (proj) {
            // error() << "Adding to cache" << mParseCount << mPath;
            proj->addToCache(mPath, mArgs, mUnit, mParseCount);
        }
    }
    mFinished(mPath, id());
}
void CompletionJob::execute()
{
    CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(),
                                  mUnsaved.isEmpty() ? 0 : mUnsaved.constData(),
                                  static_cast<unsigned long>(mUnsaved.size()) };

    CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn,
                                                          &unsavedFile, mUnsaved.isEmpty() ? 0 : 1,
                                                          CXCodeComplete_IncludeMacros
                                                          | CXCodeComplete_IncludeCodePatterns);

    if (results) {
        CompletionNode *nodes = new CompletionNode[results->NumResults];
        int nodeCount = 0;
        Map<Token, int> tokens;
        if (!mUnsaved.isEmpty()) {
            tokenize(mUnsaved.constData(), mUnsaved.size(), tokens);
            // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
            //     error() << ByteArray(it->first.data, it->first.length) << it->second;
            // }
        }
        for (unsigned i = 0; i < results->NumResults; ++i) {
            const CXCursorKind kind = results->Results[i].CursorKind;
            if (kind == CXCursor_Destructor)
                continue;

            const CXCompletionString &string = results->Results[i].CompletionString;
            const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string);
            if (availabilityKind != CXAvailability_Available)
                continue;

            const int priority = clang_getCompletionPriority(string);
            if (priority >= 75)
                continue;

            CompletionNode &node = nodes[nodeCount];
            node.priority = priority;
            node.signature.reserve(256);
            const int chunkCount = clang_getNumCompletionChunks(string);
            bool ok = true;
            for (int j=0; j<chunkCount; ++j) {
                const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j);
                if (chunkKind == CXCompletionChunk_TypedText) {
                    node.completion = RTags::eatString(clang_getCompletionChunkText(string, j));
                    if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) {
                        ok = false;
                        break;
                    }
                    node.signature.append(node.completion);
                } else {
                    node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j)));
                    if (chunkKind == CXCompletionChunk_ResultType)
                        node.signature.append(' ');
                }
            }

            if (ok) {
                int ws = node.completion.size() - 1;
                while (ws >= 0 && isspace(node.completion.at(ws)))
                    --ws;
                if (ws >= 0) {
                    node.completion.truncate(ws + 1);
                    node.signature.replace("\n", "");
                    node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1);
                    ++nodeCount;
                    continue;
                }
            }
            node.completion.clear();
            node.signature.clear();
        }
        if (nodeCount) {
            qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode);
            write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData());
            for (int i=1; i<nodeCount; ++i) {
                write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData());
            }
        }

        delete[] nodes;

        //processDiagnostics(results);

        clang_disposeCodeCompleteResults(results);
        shared_ptr<Project> proj = project();
        if (proj)
            proj->addToCache(mPath, mArgs, mIndex, mUnit);
    }
    mFinished(mPath);
}
Example #11
0
Process::ExecState Process::startInternal(const Path &command, const List<String> &a, const List<String> &environ,
                                          int timeout, unsigned execFlags)
{
    mErrorString.clear();

    Path cmd = findCommand(command);
    if (cmd.isEmpty()) {
        mErrorString = "Command not found";
        return Error;
    }
    List<String> arguments = a;
    int err;

    int closePipe[2];
    eintrwrap(err, ::pipe(closePipe));
#ifdef HAVE_CLOEXEC
    if (!SocketClient::setFlags(closePipe[1], FD_CLOEXEC, F_GETFD, F_SETFD)) {
        mErrorString = "Unable to set FD_CLOEXEC";
        eintrwrap(err, ::close(closePipe[0]));
        eintrwrap(err, ::close(closePipe[1]));
        return Error;
    }
#else
#warning No CLOEXEC, Process might have problematic behavior
#endif

    eintrwrap(err, ::pipe(mStdIn));
    eintrwrap(err, ::pipe(mStdOut));
    eintrwrap(err, ::pipe(mStdErr));
    if (mMode == Sync)
        eintrwrap(err, ::pipe(mSync));

    const char **args = new const char*[arguments.size() + 2];
    // const char* args[arguments.size() + 2];
    args[arguments.size() + 1] = 0;
    args[0] = cmd.nullTerminated();
    int pos = 1;
    for (List<String>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
        args[pos] = it->nullTerminated();
        //printf("arg: '%s'\n", args[pos]);
        ++pos;
    }

    const bool hasEnviron = !environ.empty();

    const char **env = new const char*[environ.size() + 1];
    env[environ.size()] = 0;

    if (hasEnviron) {
        pos = 0;
        //printf("fork, about to exec '%s'\n", cmd.nullTerminated());
        for (List<String>::const_iterator it = environ.begin(); it != environ.end(); ++it) {
            env[pos] = it->nullTerminated();
            //printf("env: '%s'\n", env[pos]);
            ++pos;
        }
    }

    mPid = ::fork();
    if (mPid == -1) {
        //printf("fork, something horrible has happened %d\n", errno);
        // bail out
        eintrwrap(err, ::close(mStdIn[1]));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::close(mStdOut[0]));
        eintrwrap(err, ::close(mStdErr[1]));
        eintrwrap(err, ::close(mStdErr[0]));
        eintrwrap(err, ::close(closePipe[1]));
        eintrwrap(err, ::close(closePipe[0]));
        mErrorString = "Fork failed";
        delete[] env;
        delete[] args;
        return Error;
    } else if (mPid == 0) {
        //printf("fork, in child\n");
        // child, should do some error checking here really
        eintrwrap(err, ::close(closePipe[0]));
        eintrwrap(err, ::close(mStdIn[1]));
        eintrwrap(err, ::close(mStdOut[0]));
        eintrwrap(err, ::close(mStdErr[0]));

        eintrwrap(err, ::close(STDIN_FILENO));
        eintrwrap(err, ::close(STDOUT_FILENO));
        eintrwrap(err, ::close(STDERR_FILENO));

        eintrwrap(err, ::dup2(mStdIn[0], STDIN_FILENO));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::dup2(mStdOut[1], STDOUT_FILENO));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::dup2(mStdErr[1], STDERR_FILENO));
        eintrwrap(err, ::close(mStdErr[1]));

        int ret;
        if (!mChRoot.isEmpty() && ::chroot(mChRoot.constData())) {
            goto error;
        }
        if (!mCwd.isEmpty() && ::chdir(mCwd.constData())) {
            goto error;
        }
        if (hasEnviron) {
            ret = ::execve(cmd.nullTerminated(), const_cast<char* const*>(args), const_cast<char* const*>(env));
        } else {
            ret = ::execv(cmd.nullTerminated(), const_cast<char* const*>(args));
        }
        // notify the parent process
  error:
        const char c = 'c';
        eintrwrap(err, ::write(closePipe[1], &c, 1));
        eintrwrap(err, ::close(closePipe[1]));
        ::_exit(1);
        (void)ret;
        //printf("fork, exec seemingly failed %d, %d %s\n", ret, errno, strerror(errno));
    } else {
        delete[] env;
        delete[] args;

        // parent
        eintrwrap(err, ::close(closePipe[1]));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::close(mStdErr[1]));

        //printf("fork, in parent\n");

        int flags;
        eintrwrap(flags, fcntl(mStdIn[1], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdIn[1], F_SETFL, flags | O_NONBLOCK));
        eintrwrap(flags, fcntl(mStdOut[0], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdOut[0], F_SETFL, flags | O_NONBLOCK));
        eintrwrap(flags, fcntl(mStdErr[0], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdErr[0], F_SETFL, flags | O_NONBLOCK));

        // block until exec is called in the child or until exec fails
        {
            char c;
            eintrwrap(err, ::read(closePipe[0], &c, 1));
            (void)c;

            if (err == -1) {
                // bad
                eintrwrap(err, ::close(closePipe[0]));
                mErrorString = "Failed to read from closePipe during process start";
                mPid = -1;
                return Error;
            } else if (err == 0) {
                // process has started successfully
                eintrwrap(err, ::close(closePipe[0]));
            } else if (err == 1) {
                // process start failed
                eintrwrap(err, ::close(closePipe[0]));
                mErrorString = "Process failed to start";
                mPid = -1;
                return Error;
            }
        }

        ProcessThread::addPid(mPid, this, (mMode == Async));

        //printf("fork, about to add fds: stdin=%d, stdout=%d, stderr=%d\n", mStdIn[1], mStdOut[0], mStdErr[0]);
        if (mMode == Async) {
            if (EventLoop::SharedPtr loop = EventLoop::eventLoop()) {
                loop->registerSocket(mStdOut[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2));
                loop->registerSocket(mStdErr[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2));
            }
        } else {
            // select and stuff
            timeval started, now, *selecttime = 0;
            if (timeout > 0) {
                Rct::gettime(&started);
                now = started;
                selecttime = &now;
                Rct::timevalAdd(selecttime, timeout);
            }
            if (!(execFlags & NoCloseStdIn)) {
                closeStdIn(CloseForce);
                mWantStdInClosed = false;
            }
            for (;;) {
                // set up all the select crap
                fd_set rfds, wfds;
                FD_ZERO(&rfds);
                FD_ZERO(&wfds);
                int max = 0;
                FD_SET(mStdOut[0], &rfds);
                max = std::max(max, mStdOut[0]);
                FD_SET(mStdErr[0], &rfds);
                max = std::max(max, mStdErr[0]);
                FD_SET(mSync[0], &rfds);
                max = std::max(max, mSync[0]);
                if (mStdIn[1] != -1) {
                    FD_SET(mStdIn[1], &wfds);
                    max = std::max(max, mStdIn[1]);
                }
                int ret;
                eintrwrap(ret, ::select(max + 1, &rfds, &wfds, 0, selecttime));
                if (ret == -1) { // ow
                    mErrorString = "Sync select failed: ";
                    mErrorString += strerror(errno);
                    return Error;
                }
                // check fds and stuff
                if (FD_ISSET(mStdOut[0], &rfds))
                    handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
                if (FD_ISSET(mStdErr[0], &rfds))
                    handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);
                if (mStdIn[1] != -1 && FD_ISSET(mStdIn[1], &wfds))
                    handleInput(mStdIn[1]);
                if (FD_ISSET(mSync[0], &rfds)) {
                    // we're done
                    {
                        std::lock_guard<std::mutex> lock(mMutex);
                        assert(mSync[1] == -1);

                        // try to read all remaining data on stdout and stderr
                        handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
                        handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

                        closeStdOut();
                        closeStdErr();

                        int w;
                        eintrwrap(w, ::close(mSync[0]));
                        mSync[0] = -1;
                    }
                    mFinished(this);
                    return Done;
                }
                if (timeout) {
                    assert(selecttime);
                    Rct::gettime(selecttime);
                    const int lasted = Rct::timevalDiff(selecttime, &started);
                    if (lasted >= timeout) {
                        // timeout, we're done
                        kill(); // attempt to kill
                        mErrorString = "Timed out";
                        return TimedOut;
                    }
                    *selecttime = started;
                    Rct::timevalAdd(selecttime, timeout);
                }
            }
        }
    }
    return Done;
}