Adding Semaphores Part Two

Creating a Semaphore

OSCreateSemaphore task an EventBlock from the free list and initializes it with the semaphores count. It then returns it to the user. The user is responsible for keeping track of the EventBlock and deleting it with OSDeleteSemaphore when they are finished with it.

EventBlock* OSCreateSemaphore(int count)
{
        CPU_SR cpu_sr = 0;

        ENTER_CRITICAL();
        if (eventBlockFreeList != 0)
        {
                // Take a event block from the free list and initialize it
                EventBlock* newEvent = eventBlockFreeList;
                eventBlockFreeList = newEvent->next;

                newEvent->count = count;
                newEvent->next = 0;
                newEvent->waitListHead = 0;
                newEvent->waitListTail = 0;
                EXIT_CRITICAL();
                return newEvent;
        }
        else
        {
                EXIT_CRITICAL();
                OSError("OSCreateSemaphore: Can't create new semaphore. No more free Event blocks.\n");
                return 0;
        }
}

Pending on a Semaphore

Pends on a semaphore by trying to acquire a semaphore for the calling task. If there are none available it blocks the task by removing it from the runLit and adding it to the event’s wait list. For now the wait list is First In First Out.

void OSPendSemaphore(EventBlock* eventBlock)
{
    CPU_SR cpu_sr = 0;

    ENTER_CRITICAL();
    if (eventBlock->count > 0)
    {
        // Semaphore is count is great then 0 is the task is
        // able to acquire it so decrement the count and return.
        eventBlock->count--;
        EXIT_CRITICAL();
        return;
    }

    // Semaphore is 0 so block the current task by removing it from runlist,
    // adding it to event block wait list then call scheduler.
    TaskBlock* task = _OSRemoveCurrentTaskFromRunList();
    _OSAddTaskToEventWaitList(eventBlock, task);

    // The current task was block so time to call the scheduler
    // and switch context.
    SwitchContext();
    EXIT_CRITICAL();
}

Posting to a Semaphore

Posts to a semaphore. If there are waiting tasks, the first task will acquire the semaphore and be moved to the runList. If there are not waiting tasks the semaphore is simply increment.

void OSPostSemaphore(EventBlock* eventBlock)
{
    CPU_SR cpu_sr = 0;

    ENTER_CRITICAL();
    //
    // If there are no tasks waiting then just increment the
    // count and return.
    //
    if (eventBlock->waitListHead == 0)
    {
        eventBlock->count++;
        EXIT_CRITICAL();
        return;
    }

    //
    // If there was at least one task, pop the task at the head of
    // the list, add it to the runList and call the scheduler.
    // We don't change the count because the semaphore was posted
    // and acquired at the same time.
    //
    TaskBlock* task = eventBlock->waitListHead;
    eventBlock->waitListHead = task->next;

    if (eventBlock->waitListHead == 0)
    {
        eventBlock->waitListTail = 0;
    }

    _OSAddTaskToRunList(task);

    // A task was unblocked and added to the runlist.
    // Depending on the scheduler policy we might want to call
    // for a reschedule and context switch.
    // SwitchContext();

    EXIT_CRITICAL();
}

Deleting a Semaphore

Deletes a semaphore by making all waiting tasks runnable then returning the EventBlock to the EventBlock free list.

void OSDeleteSemaphore(EventBlock* eventBlock)
{
        CPU_SR cpu_sr = 0;
        ENTER_CRITICAL();

        //
        // Make all the waiting tasks runnable by moving them to the runList.
        // Best practice is not to have any waiting task when deleting a
        // semaphore.
        //
        TaskBlock* current = eventBlock->waitListHead;

        while (current != 0)
        {
                TaskBlock* next = current->next;
                _OSAddTaskToRunList(current);
                current = next;
        }

        _OSAddEventBlockToFreeList(eventBlock);

SwitchContext();
EXIT_CRITICAL();
}