Ignorant
Ignorant

Reputation: 2671

What does a critical section have to do with counting semaphores?

Some people say that you should use mutexes to protect shared resources (i.e. critical sections), while semaphores should be used for signaling and not vice versa. So, as far as I understand, semaphores have nothing to do with critical sections.

However many other articles, including Wikipedia state that semaphores are used to solve critical section problems. Is it actually more correct to say that binary semaphores solve critical section problems since they basically act like mutexes, while counting semaphores are a different thing and don't belong to the "resource protectors" category?

Upvotes: 1

Views: 217

Answers (1)

Jerry Coffin
Jerry Coffin

Reputation: 490048

Counting semaphores can be used for resource protection, but they're typically used for different resources in different ways from a mutex semaphore.

A typical example would be a queue. For a dynamically sized queue, you have a counted semaphore to track how many items are currently in the queue. Consumers wait on that semaphore to tell them when they can read an item from the queue.

For a fixed-size queue, you add a second counted semaphore tracking the amount of empty space in the queue. Writers wait on it to determine when they're allowed to push an item to the queue.

You do often use a mutex semaphore in conjunction with those to assure that only a single thread modifies the queue itself at any given time.

For example, here's some code for a fixed-size queue using Win32 counted semaphores (and a mutex):

#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED

#include <windows.h>

template<class T, unsigned max = 256>
class queue { 
    HANDLE space_avail; // at least one slot empty
    HANDLE data_avail;  // at least one slot full
    CRITICAL_SECTION mutex; // protect buffer, in_pos, out_pos

    T buffer[max];
    long in_pos, out_pos;
public:
    queue() : in_pos(0), out_pos(0) { 
        space_avail = CreateSemaphore(NULL, max, max, NULL);
        data_avail = CreateSemaphore(NULL, 0, max, NULL);
        InitializeCriticalSection(&mutex);
    }

    void push(T data) { 
        WaitForSingleObject(space_avail, INFINITE);       
        EnterCriticalSection(&mutex);
        buffer[in_pos] = data;
        in_pos = (in_pos + 1) % max;
        LeaveCriticalSection(&mutex);
        ReleaseSemaphore(data_avail, 1, NULL);
    }

    T pop() { 
        WaitForSingleObject(data_avail,INFINITE);
        EnterCriticalSection(&mutex);
        T retval = buffer[out_pos];
        out_pos = (out_pos + 1) % max;
        LeaveCriticalSection(&mutex);
        ReleaseSemaphore(space_avail, 1, NULL);
        return retval;
    }

    ~queue() { 
        DeleteCriticalSection(&mutex);
        CloseHandle(data_avail);
        CloseHandle(space_avail);
    }
};

#endif

Upvotes: 1

Related Questions