Unix (Solaris) Threads and Semaphores
This document describes the system calls to create and manage
threads
in the Solaris version of Unix. This calls are similar to those
found in Windows NT and Posix (attempt to standardize
unix).
-
Threads
-
Thread type
-
Prototype function for a thread
-
Thread Creation constants
-
Create a thread
-
Wait for a thread
-
Other thread calls
-
Semaphores
-
Semaphore type
-
Initializing a semaphore
-
Semaphore operations
Threads [top]
Thread Type [top]
The thread header file to include is thread.h. This header file contains
the definition of a type, thread_t. This type is basically
an integer (On hawk, it is just defined as an
unsigned int. Its
use is as a thread identifier.
Example: Declare two variables t1, and t2 to hold thread id's:
#include <thread.h>
thread_t t1, t2;
Prototype function for a thread [top]
The system call that creates a thread is passed the name of a function
in the program code which that thread will execute. The prototype of this
function must be like this function declaration of f. That is, one parameter
of type void * and a return type, also void *. This is
not a great restriction since the parameter can be the
address of
any item. It does generally require that the function use a conversion
(or cast) on the parameter before using it.
void * f(void *p);
If f expects p to be a pointer to a double, then f might be written
like this:
void * f(void *p)
{
double *dp = (double *) p;
double y;
y = *dp;
// do something with the double value in y
...
return NULL;
}
Thread Creation Constants [top]
When a thread is to be created in Solaris to run independently of the thread
that creates it, a flag needs to be passed to the system call to let the
kernel know this. Otherwise the kernel will only schedule the new thread
when the calling thread finishes. The value to pass is defined as a symbolic
constant in thread.h and is of type
long.
#include <thread.h>
long flags;
flags = THR_BOUND;
Sometimes the calling thread does not want the new thread to be READY to
execute at creation. For example, the calling thread may want to create
more threads first and do other initialization. Finally after all that
is completed, then all the new threads could be made READY and thus elligible
to execute. The default is that new threads are READY. The constant THR_SUSPENDED
used for the creation flag causes the new thread to be blocked and thus
not yet elligible to run. To use both settings, the bitwise or operator
on the constants causes the corresponding bits for each one to be set in
the flags variable:
#include <thread.h>
long flags;
flags = THR_BOUND | THR_SUSPENDED;
Create a thread [top]
The system call to create a thread is:
int thr_create(void * pcallstack,
size_t stack_size,
void * (*thr_func)(void *),
void * pthr_arg,
long flags,
thread_t *pthr_id);
where,
pcallstack |
the process address to use for this thread's stack. A value of 0 lets
thr_create choose this address for the caller. |
stack_size |
the size of the thread's stack. A value of 0 lets thr_create choose
this size for the caller. |
thr_func |
the function for the thread to execute |
pthr_arg |
the argument to pass to the thread's function when it starts flags
the creation flags |
pthr_id |
the address of a thread_t variable. the function will copy
the new thread id into this variable. |
The return value is 0 if successful.
Wait for a thread [top]
A thread that creates other threads can wait for any one of these threads
to finish or for a particular one to finish by calling the following function.
int thr_join(thread_t waitfor_thr, thread_t *pfinshed_thr,
void **status);
where,
waitfor_thr |
thread id of a particular thread to wait for. Can be 0. |
pfinished_thr |
address of a thread id variable. If waitfor_thr is 0, then thr_join
waits for any thread in the process to finish. This thread's id is copied
into this variable. |
status |
Address of a variable. If not 0, thr_join copies the exit status of
the terminated thread. |
Other Thread Calls [top]
A running thread can get its own thread id by calling
thr_self().
A thread can exit but not terminate any other thread in the process by
calling thr_exit(): A thread can yield the processor for one time
by calling thr_yield().
void thr_exit(void *status);
thread_t thr_self();
void thr_yield();
where status is either 0 or the address of a variable in which
to store the exit status of the thread.
Semaphores [top]
Semaphore type [top]
The synch.h header file contains the definition of a semaphore type, sema_t.
This type is a structure with several data members. However, you will not
directly use any of the data members. The only way to use a semaphore is
through the two functions that provide atomic operations on the semaphore.
You can use the semaphore type to declare a semaphore variable, but it
is not properly initialized by this declaration. A separate system call
is the only way to properly initialize the semaphore.
Example.
#include <synch.h>
sema_t s;
Initializing a semaphore [top]
The system call to initialize a semaphore is sema_init.
int sema_init(sema_t *sp, unsigned int count,
int type, void * arg);
where,
sp |
address of the semaphore variable to be initialized |
count |
the initial value of the integer part of the semaphore |
type |
a constant to indicate in processes or accross process use. |
arg |
This address should be 0. Currently it is not used. |
The return value is 0 if successful.
Example:
#include <synch.h>
sema_t s;
if ( sema_init(&s, 1, USYNC_THREAD, 0) != 0 )
{
// Error: initialization failed
}
Semaphore Operations [top]
The two operations on a properly initialized semaphore are
sema_wait
and sema_post:
int sema_wait(sema_t *sp);
int sema_post(sema_t *sp);
Example:
sema_t s;
// Assume sema_init has been called to initialize s.
sema_wait(&s);
....
sema_post(&s);