static NTSTATUS ProcessRunnable( PEPOLL_THREAD pThread, PRING pRunnable, PRING pTimed, PRING pWaiting, LONG64 llNow ) { NTSTATUS status = STATUS_SUCCESS; ULONG ulTicks = MAX_TICKS; PLW_TASK pTask = NULL; PLW_TASK_GROUP pGroup = NULL; PRING pRing = NULL; PRING pNext = NULL; /* We are guaranteed to run each task at least once. If tasks remain on the runnable list by yielding, we will continue to run them all in a round robin until our ticks are depleted. */ while (ulTicks && !RingIsEmpty(pRunnable)) { for (pRing = pRunnable->pNext; pRing != pRunnable; pRing = pNext) { pNext = pRing->pNext; pTask = LW_STRUCT_FROM_FIELD(pRing, EPOLL_TASK, QueueRing); RunTask(pTask, llNow); if (ulTicks) { ulTicks--; } if (pTask->EventWait != LW_TASK_EVENT_COMPLETE) { /* Task is still waiting to be runnable, update events in epoll set */ status = UpdateEventWait( pTask, pThread->EpollFd ); GOTO_ERROR_ON_STATUS(status); if (pTask->EventWait & LW_TASK_EVENT_YIELD) { /* Task is yielding. Set YIELD in its trigger arguments and and leave it on the runnable list for the next iteration */ pTask->EventArgs |= LW_TASK_EVENT_YIELD; } else if (pTask->EventWait & LW_TASK_EVENT_TIME) { /* If the task is waiting for a timeout, insert it into the timed queue */ RingRemove(&pTask->QueueRing); InsertTimedQueue(pTimed, pTask); } else { /* Otherwise, put it in the generic waiting queue */ RingRemove(&pTask->QueueRing); RingEnqueue(pWaiting, &pTask->QueueRing); } } else { /* Task is complete */ RingRemove(&pTask->QueueRing); /* Turn off any fd in the epoll set */ if (pTask->Fd >= 0) { status = LwRtlSetTaskFd(pTask, pTask->Fd, 0); GOTO_ERROR_ON_STATUS(status); } /* Unsubscribe task from any UNIX signals */ if (pTask->pUnixSignal) { RegisterTaskUnixSignal(pTask, 0, FALSE); } LOCK_POOL(pThread->pPool); pThread->ulLoad--; UNLOCK_POOL(pThread->pPool); pGroup = pTask->pGroup; /* If task was in a task group, remove it and notify anyone waiting on the group */ if (pGroup) { LOCK_GROUP(pGroup); pTask->pGroup = NULL; RingRemove(&pTask->GroupRing); pthread_cond_broadcast(&pGroup->Event); UNLOCK_GROUP(pGroup); } LOCK_THREAD(pThread); if (--pTask->ulRefCount) { /* The task still has a reference, so mark it as completed and notify anyone waiting on it */ pTask->EventSignal = TASK_COMPLETE_MASK; pthread_cond_broadcast(&pThread->Event); UNLOCK_THREAD(pThread); } else { /* We held the last reference to the task, so delete it */ RingRemove(&pTask->SignalRing); UNLOCK_THREAD(pThread); TaskDelete(pTask); } } } } error: return status; }
static VOID Transceiver( PLW_TASK pTask, PVOID pContext, LW_TASK_EVENT_MASK WakeMask, LW_TASK_EVENT_MASK* pWaitMask, PLONG64 pllTime ) { PSOCKET pSocket = (PSOCKET) pContext; size_t sendSize = gpSettings->ulBufferSize / gpSettings->usSendSegments; ssize_t transferred = 0; NTSTATUS status; long opts = 0; int err = 0; if (WakeMask & LW_TASK_EVENT_CANCEL || pSocket->Iteration >= gpSettings->ulIterations) { *pWaitMask = 0; status = LwRtlSetTaskFd(pTask, pSocket->Fd, 0); ASSERT_SUCCESS(status); close(pSocket->Fd); pSocket->Fd = -1; return; } else if (WakeMask & LW_TASK_EVENT_INIT) { assert(pSocket->Iteration == 0); /* Put socket in nonblock mode */ opts = fcntl(pSocket->Fd, F_GETFL, 0); assert(opts >= 0); opts |= O_NONBLOCK; err = fcntl(pSocket->Fd, F_SETFL, opts); LW_ASSERT(err == 0); status = LwRtlSetTaskFd( pTask, pSocket->Fd, LW_TASK_EVENT_FD_READABLE | LW_TASK_EVENT_FD_WRITABLE); ASSERT_SUCCESS(status); } switch(pSocket->State) { case STATE_SEND: if (sendSize > gpSettings->ulBufferSize - pSocket->Position) sendSize = gpSettings->ulBufferSize - pSocket->Position; transferred = write( pSocket->Fd, pSocket->pBuffer + pSocket->Position, sendSize); assert(transferred >= 0 || errno == EAGAIN); if (transferred < 0 && errno == EAGAIN) { *pWaitMask = LW_TASK_EVENT_FD_WRITABLE; break; } pSocket->Position += transferred; pSocket->ullTotalTransferred += transferred; if (pSocket->Position >= gpSettings->ulBufferSize) { pSocket->State = STATE_RECV; pSocket->Position = 0; pSocket->Iteration++; } *pWaitMask = LW_TASK_EVENT_YIELD; break; case STATE_RECV: transferred = read( pSocket->Fd, pSocket->pBuffer + pSocket->Position, gpSettings->ulBufferSize - pSocket->Position); assert(transferred >= 0 || errno == EAGAIN); if (transferred < 0 && errno == EAGAIN) { *pWaitMask = LW_TASK_EVENT_FD_READABLE; break; } pSocket->Position += transferred; pSocket->ullTotalTransferred += transferred; if (pSocket->Position >= gpSettings->ulBufferSize) { pSocket->State = STATE_SEND; pSocket->Position = 0; pSocket->Iteration++; } *pWaitMask = LW_TASK_EVENT_YIELD; break; } }
static VOID ProcessRunnable( PKQUEUE_THREAD pThread, PKQUEUE_COMMANDS pCommands, PRING pRunnable, PRING pTimed, PRING pWaiting, LONG64 llNow ) { ULONG ulTicks = MAX_TICKS; PLW_TASK pTask = NULL; PLW_TASK_GROUP pGroup = NULL; PRING pRing = NULL; PRING pNext = NULL; /* We are guaranteed to run each task at least once. If tasks remain on the runnable list by yielding, we will continue to run them all in a round robin until our ticks are depleted. */ while (ulTicks && !RingIsEmpty(pRunnable)) { for (pRing = pRunnable->pNext; pRing != pRunnable; pRing = pNext) { pNext = pRing->pNext; pTask = LW_STRUCT_FROM_FIELD(pRing, KQUEUE_TASK, QueueRing); RunTask(pTask, llNow); if (ulTicks) { ulTicks--; } if (pTask->EventWait != LW_TASK_EVENT_COMPLETE) { if (pTask->EventWait & LW_TASK_EVENT_YIELD) { /* Task is yielding. Set the YIELD flag and leave it on the runnable list for the next iteration. */ pTask->EventArgs |= LW_TASK_EVENT_YIELD; } else { /* Task is still waiting on events, update kqueue */ UpdateEventWait(pCommands, pTask); if (pTask->EventWait & LW_TASK_EVENT_TIME) { /* If the task is waiting for a timeout, insert it into the timed queue */ RingRemove(&pTask->QueueRing); InsertTimedQueue(pTimed, pTask); } else { /* Otherwise, put it in the generic waiting queue */ RingRemove(&pTask->QueueRing); RingEnqueue(pWaiting, &pTask->QueueRing); } } } else { /* Task is complete */ RingRemove(&pTask->QueueRing); /* Remove any associated events from the kqueue */ if (pTask->Fd >= 0) { (void) LwRtlSetTaskFd(pTask, pTask->Fd, 0); } /* Unsubscribe task from any UNIX signals */ if (pTask->pUnixSignal) { RegisterTaskUnixSignal(pTask, 0, FALSE); } LOCK_POOL(pThread->pPool); pThread->ulLoad--; UNLOCK_POOL(pThread->pPool); pGroup = pTask->pGroup; /* If task was in a task group, remove it and notify anyone waiting on the group */ if (pGroup) { LOCK_GROUP(pGroup); pTask->pGroup = NULL; RingRemove(&pTask->GroupRing); pthread_cond_broadcast(&pGroup->Event); UNLOCK_GROUP(pGroup); } LOCK_THREAD(pThread); if (--pTask->ulRefCount) { /* The task still has a reference, so mark it as completed and notify anyone waiting on it */ pTask->EventSignal = TASK_COMPLETE_MASK; pthread_cond_broadcast(&pThread->Event); UNLOCK_THREAD(pThread); } else { /* We held the last reference to the task, so delete it */ RingRemove(&pTask->SignalRing); UNLOCK_THREAD(pThread); TaskDelete(pTask); } } } } /* Update kevent commands for yielding tasks */ for (pRing = pRunnable->pNext; pRing != pRunnable; pRing = pRing->pNext) { pTask = LW_STRUCT_FROM_FIELD(pRing, KQUEUE_TASK, QueueRing); if (pTask->EventArgs & LW_TASK_EVENT_YIELD) { UpdateEventWait(pCommands, pTask); } } }
static VOID LogTapper( PLW_TASK pTask, PVOID pContext, LW_TASK_EVENT_MASK WakeMask, LW_TASK_EVENT_MASK* pWaitMask, LONG64* pllTime ) { NTSTATUS status = STATUS_SUCCESS; int FifoFd = *(int*) pContext; siginfo_t info = {0}; char buffer[2048] = {0}; ssize_t count = 0; if (WakeMask & LW_TASK_EVENT_CANCEL) { status = STATUS_CANCELLED; BAIL_ON_ERROR(status); } else if (WakeMask & LW_TASK_EVENT_INIT) { status = LwRtlSetTaskUnixSignal(pTask, SIGINT, TRUE); BAIL_ON_ERROR(status); status = LwRtlSetTaskUnixSignal(pTask, SIGTERM, TRUE); BAIL_ON_ERROR(status); status = LwRtlSetTaskFd(pTask, FifoFd, LW_TASK_EVENT_FD_READABLE); BAIL_ON_ERROR(status); } else if (WakeMask & LW_TASK_EVENT_UNIX_SIGNAL) { while (LwRtlNextTaskUnixSignal(pTask, &info)) { if (info.si_signo == SIGINT || info.si_signo == SIGTERM) { status = STATUS_CANCELLED; BAIL_ON_ERROR(status); } } } else if (WakeMask & LW_TASK_EVENT_FD_READABLE) { do { do { count = read(FifoFd, buffer, sizeof(buffer)); } while (count < 0 && errno == EINTR); if (count == 0) { status = STATUS_END_OF_FILE; BAIL_ON_ERROR(status); } else if (count > 0) { count = write(1, buffer, count); if (count < 0) { status = LwErrnoToNtStatus(errno); BAIL_ON_ERROR(status); } } else if (errno != EAGAIN) { status = LwErrnoToNtStatus(errno); BAIL_ON_ERROR(status); } } while (count > 0); } *pWaitMask = LW_TASK_EVENT_FD_READABLE | LW_TASK_EVENT_UNIX_SIGNAL; cleanup: return; error: *pWaitMask = LW_TASK_EVENT_COMPLETE; LwRtlSetTaskFd(pTask, FifoFd, 0); LwRtlExitMain(status); goto cleanup; }