Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  Programming With Solaris Threads Similar Synchronization Functions--Semaphores  Previous   Contents   Next 
   
 

Initialize a Semaphore

sema_init(3THR)

#include <thread.h>

int sema_init(sema_t *sp, unsigned int count, int type,
    void *arg);

Use sema_init(3THR) to initialize the semaphore variable pointed to by sp by count amount. type can be one of the following (note that arg is currently ignored).

USYNC_PROCESS The semaphore can be used to synchronize threads in this process and other processes. Only one process should initialize the semaphore. arg is ignored.

USYNC_THREAD The semaphore can be used to synchronize threads in this process, only. arg is ignored.

Multiple threads must not initialize the same semaphore simultaneously. A semaphore must not be reinitialized while other threads might be using it.

Semaphores With Intraprocess Scope

#include <thread.h>

sema_t sp;
int ret;
int count;
count = 4;

/* to be used within this process only */
ret = sema_init(&sp, count, USYNC_THREAD, 0); 

Semaphores With Interprocess Scope

#include <thread.h>

sema_t sp;
int ret;
int count;
count = 4;

/* to be used among all the processes */
ret = sema_init (&sp, count, USYNC_PROCESS, 0); 

Increment a Semaphore

sema_post(3THR)

#include <thread.h>

int sema_post(sema_t *sp);

Use sema_post(3THR) to atomically increment the semaphore pointed to by sp. When any threads are blocked on the semaphore, one is unblocked.

Block on a Semaphore Count

sema_wait(3THR)

#include <thread.h>

int sema_wait(sema_t *sp);

Use sema_wait(3THR) to block the calling thread until the count in the semaphore pointed to by sp becomes greater than zero, then atomically decrement it.

Decrement a Semaphore Count

sema_trywait(3THR)

#include <thread.h>

int sema_trywait(sema_t *sp);

Use sema_trywait(3THR) to atomically decrement the count in the semaphore pointed to by sp when the count is greater than zero. This function is a nonblocking version of sema_wait().

Destroy the Semaphore State

sem_destroy(3THR)

#include <thread.h>

int sema_destroy(sema_t *sp);

Use sem_destroy(3THR) to destroy any state associated with the semaphore pointed to by sp. The space for storing the semaphore is not freed.

Synchronization Across Process Boundaries

Each of the synchronization primitives can be set up to be used across process boundaries. This is done quite simply by ensuring that the synchronization variable is located in a shared memory segment and by calling the appropriate init routine with type set to USYNC_PROCESS.

If this has been done, then the operations on the synchronization variables work just as they do when type is USYNC_THREAD.

mutex_init(&m, USYNC_PROCESS, 0);
rwlock_init(&rw, USYNC_PROCESS, 0);
cond_init(&cv, USYNC_PROCESS, 0);
sema_init(&s, count, USYNC_PROCESS, 0);

Using LWPs Between Processes

Using locks and condition variables between processes does not require using the threads library. The recommended approach is to use the threads library interfaces, but when this is not desirable, then the _lwp_mutex_* and _lwp_cond_* interfaces can be used as follows:

  1. Allocate the locks and condition variables as usual in shared memory (either with shmop(2) or mmap(2)).

  2. Then initialize the newly allocated objects appropriately with the USYNC_PROCESS type. Because no interface is available to perform the initialization (_lwp_mutex_init(2) and _lwp_cond_init(2) do not exist), the objects can be initialized using statically allocated and initialized dummy objects.

For example, to initialize lockp:

	lwp_mutex_t *lwp_lockp;
	lwp_mutex_t dummy_shared_mutex = SHAREDMUTEX;
		/* SHAREDMUTEX is defined in /usr/include/synch.h */
	...
	...
	lwp_lockp = alloc_shared_lock();
	*lwp_lockp = dummy_shared_mutex;

Similarly, for condition variables:

	lwp_cond_t *lwp_condp;
	lwp_cond_t dummy_shared_cv = SHAREDCV;
		/* SHAREDCV is defined in /usr/include/synch.h */
	...
	...
	lwp_condp = alloc_shared_cv();
	*lwp_condp = dummy_shared_cv;

Producer/Consumer Problem Example

Example 8-2 shows the producer/consumer problem with the producer and consumer in separate processes. The main routine maps zero-filled memory (that it shares with its child process) into its address space. Note that mutex_init() and cond_init() must be called because the type of the synchronization variables is USYNC_PROCESS.

A child process is created that runs the consumer. The parent runs the producer.

This example also shows the drivers for the producer and consumer. The producer_driver() simply reads characters from stdin and calls producer(). The consumer_driver() gets characters by calling consumer() and writes them to stdout.

The data structure for Example 8-2 is the same as that used for the solution with condition variables (see "Nested Locking With a Singly Linked List").


Example 8-2 The Producer/Consumer Problem, Using USYNC_PROCESS

main() {
    int zfd;
    buffer_t *buffer;

    zfd = open("/dev/zero", O_RDWR);
    buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t),
        PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0);
    buffer->occupied = buffer->nextin = buffer->nextout = 0;

    mutex_init(&buffer->lock, USYNC_PROCESS, 0);
    cond_init(&buffer->less, USYNC_PROCESS, 0);
    cond_init(&buffer->more, USYNC_PROCESS, 0);
    if (fork() == 0)
        consumer_driver(buffer);
    else
        producer_driver(buffer);
}

void producer_driver(buffer_t *b) {
    int item;

    while (1) {
        item = getchar();
        if (item == EOF) {
            producer(b, `\0');
            break;
        } else
            producer(b, (char)item);
    }
}

void consumer_driver(buffer_t *b) {
    char item;

    while (1) {
        if ((item = consumer(b)) == '\0')
            break;
        putchar(item);
    }
}

 
 
 
  Previous   Contents   Next