Esempio n. 1
0
void
NotifyTaskUnixSignal(
    PLW_TASK pTask,
    siginfo_t* pInfo
    )
{
    LOCK_THREAD(pTask->pThread);

    if (pTask->EventSignal != TASK_COMPLETE_MASK)
    {
        while (pTask->pUnixSignal->si_signo)
        {
            pthread_cond_wait(&pTask->pThread->Event, &pTask->pThread->Lock);
            if (pTask->EventSignal == TASK_COMPLETE_MASK)
            {
                goto cleanup;
            }
        }

        *pTask->pUnixSignal = *pInfo;
        pTask->EventSignal |= LW_TASK_EVENT_UNIX_SIGNAL;
        RingRemove(&pTask->SignalRing);
        RingEnqueue(&pTask->pThread->Tasks, &pTask->SignalRing);
        SignalThread(pTask->pThread);
    }

cleanup:

    UNLOCK_THREAD(pTask->pThread);
}
Esempio n. 2
0
static
VOID
ScheduleTimedTasks(
    PRING pTimed,
    LONG64 llNow,
    PRING pRunnable
    )
{
    PLW_TASK pTask = NULL;
    PRING pRing = NULL;
    PRING pNext = NULL;

    for (pRing = pTimed->pNext; pRing != pTimed; pRing = pNext)
    {
        pNext = pRing->pNext;
        pTask = LW_STRUCT_FROM_FIELD(pRing, EPOLL_TASK, QueueRing);

        /* No more tasks in the queue are past the deadline
           since the queue is sorted */
        if (pTask->llDeadline > llNow)
        {
            break;
        }

        RingRemove(&pTask->QueueRing);
        RingEnqueue(pRunnable, &pTask->QueueRing);

        pTask->EventArgs |= LW_TASK_EVENT_TIME;
    }
}
Esempio n. 3
0
VOID
LwRtlCancelTaskGroup(
    PLW_TASK_GROUP pGroup
    )
{
    PRING ring = NULL;
    PLW_TASK pTask = NULL;

    LOCK_GROUP(pGroup);

    pGroup->bCancelled = TRUE;

    LockAllThreads(pGroup->pPool);

    for (ring = pGroup->Tasks.pNext; ring != &pGroup->Tasks; ring = ring->pNext)
    {
        pTask = LW_STRUCT_FROM_FIELD(ring, EPOLL_TASK, GroupRing);

        if (pTask->EventSignal != TASK_COMPLETE_MASK)
        {
            pTask->EventSignal |= LW_TASK_EVENT_EXPLICIT | LW_TASK_EVENT_CANCEL;
            RingRemove(&pTask->SignalRing);
            RingEnqueue(&pTask->pThread->Tasks, &pTask->SignalRing);
            SignalThread(pTask->pThread);
        }
    }

    UnlockAllThreads(pGroup->pPool);
    UNLOCK_GROUP(pGroup);
}
Esempio n. 4
0
/*
 * Updates the event args on tasks from epoll results and
 * schedules them to run.
 */
static
VOID
ScheduleWaitingTasks(
    struct epoll_event* pEvents,
    int eventCount,
    LONG64 llNow,
    PRING pRunnable,
    PBOOLEAN pbSignalled
    )
{
    PLW_TASK pTask = NULL;
    int i = 0;

    *pbSignalled = FALSE;

    for (i = 0; i < eventCount; i++)
    {
        pTask = (PLW_TASK) pEvents[i].data.ptr;

        if (!pTask)
        {
            /* Event was the thread signal fd becoming active */
            *pbSignalled = TRUE;
            continue;
        }

        if ((pEvents[i].events & (EPOLLIN | EPOLLHUP)) &&
            (pTask->EventWait & LW_TASK_EVENT_FD_READABLE))
        {
            pTask->EventArgs |= LW_TASK_EVENT_FD_READABLE;
        }

        if (pEvents[i].events & EPOLLOUT)
        {
            pTask->EventArgs |= LW_TASK_EVENT_FD_WRITABLE;
        }

        if ((pEvents[i].events & EPOLLERR) &&
            (pTask->EventWait & LW_TASK_EVENT_FD_EXCEPTION))
        {
            pTask->EventArgs |= LW_TASK_EVENT_FD_EXCEPTION;
        }

        /* If the task's deadline has expired, set the time event bit */
        if (pTask->EventWait & LW_TASK_EVENT_TIME &&
            pTask->llDeadline != 0 &&
            pTask->llDeadline <= llNow)
        {
            pTask->EventArgs |= LW_TASK_EVENT_TIME;
        }

        /* Schedule task to run if it has been triggered */
        if (pTask->EventWait & pTask->EventArgs)
        {
            RingRemove(&pTask->QueueRing);
            RingEnqueue(pRunnable, &pTask->QueueRing);
        }
    }
}
/*
 * Updates the set events on tasks from kqueue results and
 * schedules them to run.
 */
static
VOID
ScheduleWaitingTasks(
    struct kevent* pEvents,
    int eventCount,
    LONG64 llNow,
    PRING pRunnable,
    PBOOLEAN pbSignalled
    )
{
    PLW_TASK pTask = NULL;
    int i = 0;

    *pbSignalled = FALSE;

    for (i = 0; i < eventCount; i++)
    {
        pTask = (PLW_TASK) pEvents[i].udata;

        if (!pTask)
        {
            /* Event was the thread signal fd becoming active */
            *pbSignalled = TRUE;
            continue;
        }

        if (pEvents[i].filter == EVFILT_READ &&
            (pTask->EventWait & LW_TASK_EVENT_FD_READABLE))
        {
            pTask->EventArgs |= LW_TASK_EVENT_FD_READABLE;
        }
        
        if (pEvents[i].filter == EVFILT_WRITE &&
            (pTask->EventWait & LW_TASK_EVENT_FD_WRITABLE))
        {
            pTask->EventArgs |= LW_TASK_EVENT_FD_WRITABLE;
        }
      
        /* FIXME: errors? */

        /* If the task's deadline has expired, set the time event bit */
        if (pTask->EventWait & LW_TASK_EVENT_TIME &&
            pTask->llDeadline != 0 &&
            pTask->llDeadline <= llNow)
        {
            pTask->EventArgs |= LW_TASK_EVENT_TIME;
        }
        
        /* Schedule task to run if it has been triggered */
        if (pTask->EventWait & pTask->EventArgs)
        {
            RingRemove(&pTask->QueueRing);
            RingEnqueue(pRunnable, &pTask->QueueRing);
        }
    }
}
Esempio n. 6
0
static
VOID
DispatchSignal(
    siginfo_t* pInfo
    )
{
    RING dispatch;
    PRING pBase = NULL;
    PRING pRing = NULL;
    PRING pNext = NULL;
    PLW_SIGNAL_SUBSCRIPTION pSub = NULL;

    if (!gSignal.pSubscribers || pInfo->si_signo > gSignal.maxSig || pInfo->si_signo < 0)
    {
        return;
    }

    RingInit(&dispatch);

    pBase = &gSignal.pSubscribers[pInfo->si_signo];
    for (pRing = pBase->pNext; pRing != pBase; pRing = pRing->pNext)
    {
        pSub = LW_STRUCT_FROM_FIELD(pRing, LW_SIGNAL_SUBSCRIPTION, Ring);
        
        pSub->ucRefCount++;
        RingInit(&pSub->DispatchRing);
        RingEnqueue(&dispatch, &pSub->DispatchRing);
    }

    UNLOCK_SIGNAL();
    for (pRing = dispatch.pNext; pRing != &dispatch; pRing = pRing->pNext)
    {
        pSub = LW_STRUCT_FROM_FIELD(pRing, LW_SIGNAL_SUBSCRIPTION, DispatchRing);
        
        NotifyTaskUnixSignal(pSub->pTask, pInfo);
    }
    LOCK_SIGNAL();

    for (pRing = dispatch.pNext; pRing != &dispatch; pRing = pNext)
    {
        pNext = pRing->pNext;
        pSub = LW_STRUCT_FROM_FIELD(pRing, LW_SIGNAL_SUBSCRIPTION, DispatchRing);
        
        if (--pSub->ucRefCount == 0)
        {
            RingRemove(&pSub->Ring);
            LwRtlReleaseTask(&pSub->pTask);
            LwRtlMemoryFree(pSub);
        }
    }
}
Esempio n. 7
0
static
VOID
ScheduleSignalled(
    PEPOLL_THREAD pThread,
    PRING pRunnable,
    PBOOLEAN pbShutdown
    )
{
    PRING pRing = NULL;
    PRING pNext = NULL;
    PLW_TASK pTask = NULL;
    char c = 0;
    int res = 0;

    LOCK_THREAD(pThread);

    if (pThread->bSignalled)
    {
        pThread->bSignalled = FALSE;

        res = read(pThread->SignalFds[0], &c, sizeof(c));
        assert(res == sizeof(c));

        /* Add all signalled tasks to the runnable list */
        for (pRing = pThread->Tasks.pNext; pRing != &pThread->Tasks; pRing = pNext)
        {
            pNext = pRing->pNext;
            pTask = LW_STRUCT_FROM_FIELD(pRing, EPOLL_TASK, SignalRing);

            RingRemove(&pTask->SignalRing);
            RingRemove(&pTask->QueueRing);

            if (pTask->EventSignal != TASK_COMPLETE_MASK)
            {
                RingEnqueue(pRunnable, &pTask->QueueRing);
                /* Transfer the signal bits into the event args */
                pTask->EventArgs |= pTask->EventSignal;
                pTask->EventSignal = 0;
            }
        }

        if (pThread->bShutdown && !*pbShutdown)
        {
            *pbShutdown = pThread->bShutdown;
        }
    }

    UNLOCK_THREAD(pThread);
}
Esempio n. 8
0
VOID
LwRtlWakeTask(
    PLW_TASK pTask
    )
{
    LOCK_THREAD(pTask->pThread);

    if (pTask->EventSignal != TASK_COMPLETE_MASK)
    {
        pTask->EventSignal |= LW_TASK_EVENT_EXPLICIT;
        RingRemove(&pTask->SignalRing);
        RingEnqueue(&pTask->pThread->Tasks, &pTask->SignalRing);
        SignalThread(pTask->pThread);
    }

    UNLOCK_THREAD(pTask->pThread);
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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);
        }
    }
}
Esempio n. 11
0
NTSTATUS
RegisterTaskUnixSignal(
    LW_IN PLW_TASK pTask,
    LW_IN int Sig,
    LW_IN LW_BOOLEAN bSubscribe
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    size_t i = 0;
    PRING pBase = NULL;
    PRING pRing = NULL;
    PLW_SIGNAL_SUBSCRIPTION pExisting = NULL;
    PLW_SIGNAL_SUBSCRIPTION pSub = NULL;
    struct sigaction action;
#ifdef SIGRTMAX
    int maxSig = SIGRTMAX;
#else
    int maxSig = SIGUSR2;
#endif

    if (Sig == 0)
    {
        for (i = 1; i < maxSig + 1; i++)
        {
            status = RegisterTaskUnixSignal(pTask, (int) i, bSubscribe);
            if (status)
            {
                return status;
            }
        }

        return STATUS_SUCCESS;
    }

    LOCK_SIGNAL();

    if (Sig > maxSig || Sig < 0)
    {
        status = STATUS_INVALID_PARAMETER;
        GOTO_ERROR_ON_STATUS(status);
    }

    if (!gSignal.pSubscribers)
    {
        status = LW_RTL_ALLOCATE_ARRAY_AUTO(&gSignal.pSubscribers, maxSig + 1);
        GOTO_ERROR_ON_STATUS(status);

        for (i = 0; i < maxSig + 1; i++)
        {
            RingInit(&gSignal.pSubscribers[i]);
        }
        
        gSignal.maxSig = maxSig;
    }

    pBase = &gSignal.pSubscribers[Sig];
    for (pRing = pBase->pNext; pRing != pBase; pRing = pRing->pNext)
    {
        pSub = LW_STRUCT_FROM_FIELD(pRing, LW_SIGNAL_SUBSCRIPTION, Ring);
        if (pSub->pTask == pTask)
        {
            pExisting = pSub;
            break;
        }
    }

    if (bSubscribe && !pExisting)
    {
        if (Sig != SIGINT)
        {
            memset(&action, 0, sizeof(action));
            
            /* Make sure there is a dummy handler for the signal
               so it is actually delivered to the process */
            action.sa_handler = DummyHandler;
            action.sa_flags = 0;
            
            if (sigaction(Sig, &action, NULL) < 0)
            {
                status = LwErrnoToNtStatus(errno);
                GOTO_ERROR_ON_STATUS(status);
            }
        }

        status = LW_RTL_ALLOCATE_AUTO(&pSub);
        GOTO_ERROR_ON_STATUS(status);

        pSub->pTask = pTask;
        pSub->ucRefCount = 1;
        RingInit(&pSub->Ring);
        RingInit(&pSub->DispatchRing);

        RingEnqueue(pBase, &pSub->Ring);

        RetainTask(pTask);
    }
    else if (!bSubscribe && pExisting)
    {
        RingRemove(&pExisting->Ring);

        if (--pExisting->ucRefCount == 0)
        {
            LwRtlReleaseTask(&pExisting->pTask);
            LwRtlMemoryFree(pExisting);
        }
    }

error:

    UNLOCK_SIGNAL();

    return status;
}
Esempio n. 12
0
VOID
ScheduleWorkItem(
    PLW_WORK_THREADS pThreads,
    PLW_WORK_ITEM pItem,
    LW_SCHEDULE_FLAGS Flags
    )
{
    size_t i = 0;

    if (pThreads == NULL)
    {
        pThreads = pItem->pThreads;
    }

    LOCK_THREADS(pThreads);
    
    assert(pThreads->ulStarted > 0);

    /* Enqueue work item */
    if (Flags & LW_SCHEDULE_HIGH_PRIORITY)
    {
        RingEnqueueFront(&pThreads->WorkItems, &pItem->Ring);
    }
    else
    {
        RingEnqueue(&pThreads->WorkItems, &pItem->Ring);
    }

    pThreads->ulQueued++;

    /*
     * If there are more pending work items than there
     * are available threads, and not all threads are started,
     * try to start another one to handle the additional load
     */
    if (pThreads->ulAvailable < pThreads->ulQueued &&
        pThreads->ulStarted < pThreads->ulWorkThreadCount)
    {
        for (i = 0; i < pThreads->ulWorkThreadCount; i++)
        {
            if (!pThreads->pWorkThreads[i].bStarted)
            {
                if (StartWorkThread(pThreads, &pThreads->pWorkThreads[i]) != STATUS_SUCCESS)
                {
                    LW_RTL_LOG_WARNING("Could not start work item thread");
                    /* Signal an existing thread instead */
                    pthread_cond_signal(&pThreads->Event);
                }
                break;
            }
        }
    }
    else if (pThreads->ulAvailable)
    {
        /* Signal an existing thread */
        pthread_cond_signal(&pThreads->Event);
    }


    UNLOCK_THREADS(pThreads);
}