void *LogTimeEntry::threadStart(void *obj) {
    prctl(PR_SET_NAME, "logd.reader.per");

    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);

    pthread_cleanup_push(threadStop, obj);

    SocketClient *client = me->mClient;
    if (!client) {
        me->error();
        return NULL;
    }

    LogBuffer &logbuf = me->mReader.logbuf();

    bool privileged = FlushCommand::hasReadLogs(client);

    lock();

    while (me->threadRunning && !me->isError_Locked()) {
        uint64_t start = me->mStart;

        unlock();

        if (me->mTail) {
            logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
        }
        start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);

        lock();

        if (start == LogBufferElement::FLUSH_ERROR) {
            me->error_Locked();
        }

        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
            break;
        }

        me->cleanSkip_Locked();

        pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
    }

    unlock();

    pthread_cleanup_pop(true);

    return NULL;
}
// A second pass to send the selected elements
int LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);

    LogTimeEntry::lock();

    me->mStart = element->getSequence();

    if (me->skipAhead[element->getLogId()]) {
        me->skipAhead[element->getLogId()]--;
        goto skip;
    }

    // Truncate to close race between first and second pass
    if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
        goto stop;
    }

    if (!me->isWatching(element->getLogId())) {
        goto skip;
    }

    if (me->mPid && (me->mPid != element->getPid())) {
        goto skip;
    }

    if (me->isError_Locked()) {
        goto stop;
    }

    if (!me->mTail) {
        goto ok;
    }

    ++me->mIndex;

    if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
        goto skip;
    }

    if (!me->mNonBlock) {
        me->mTail = 0;
    }

ok:
    if (!me->skipAhead[element->getLogId()]) {
        LogTimeEntry::unlock();
        return true;
    }
    // FALLTHRU

skip:
    LogTimeEntry::unlock();
    return false;

stop:
    LogTimeEntry::unlock();
    return -1;
}
void *LogTimeEntry::threadStart(void *obj) {
    prctl(PR_SET_NAME, "logd.reader.per");

    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);

    pthread_cleanup_push(threadStop, obj);

    SocketClient *client = me->mClient;
    if (!client) {
        me->error();
        pthread_exit(NULL);
    }

    LogBuffer &logbuf = me->mReader.logbuf();

    bool privileged = FlushCommand::hasReadLogs(client);

    lock();

    me->threadTriggered = true;

    while(me->threadTriggered && !me->isError_Locked()) {

        me->threadTriggered = false;

        log_time start = me->mStart;

        unlock();

        if (me->mTail) {
            logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
        }
        start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);

        if (start == LogBufferElement::FLUSH_ERROR) {
            me->error();
        }

        if (me->mNonBlock) {
            lock();
            break;
        }

        sched_yield();

        lock();
    }

    unlock();

    pthread_exit(NULL);

    pthread_cleanup_pop(true);

    return NULL;
}
// A second pass to send the selected elements
bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);

    LogTimeEntry::lock();

    if (me->skipAhead) {
        me->skipAhead--;
    }

    me->mStart = element->getMonotonicTime();

    // Truncate to close race between first and second pass
    if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
        goto skip;
    }

    if ((me->mLogMask & (1 << element->getLogId())) == 0) {
        goto skip;
    }

    if (me->mPid && (me->mPid != element->getPid())) {
        goto skip;
    }

    if (me->isError_Locked()) {
        goto skip;
    }

    if (!me->mTail) {
        goto ok;
    }

    ++me->mIndex;

    if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
        goto skip;
    }

    if (!me->mNonBlock) {
        me->mTail = 0;
    }

ok:
    if (!me->skipAhead) {
        LogTimeEntry::unlock();
        return true;
    }
    // FALLTHRU

skip:
    LogTimeEntry::unlock();
    return false;
}
void LogTimeEntry::threadStop(void *obj) {
    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);

    lock();

    if (me->mNonBlock) {
        me->error_Locked();
    }

    SocketClient *client = me->mClient;

    if (me->isError_Locked()) {
        LogReader &reader = me->mReader;
        LastLogTimes &times = reader.logbuf().mTimes;

        LastLogTimes::iterator it = times.begin();
        while(it != times.end()) {
            if (*it == me) {
                times.erase(it);
                me->release_Locked();
                break;
            }
            it++;
        }

        me->mClient = NULL;
        reader.release(client);
    }

    if (client) {
        client->decRef();
    }

    me->threadRunning = false;
    me->decRef_Locked();

    unlock();
}