RU/2: Форум. Общение пользователей и разработчиков OS/2 (eCS). : Multithreading (XV)


Список сообщений | Написать новое | Ответить на сообщение | Домой Поиск:
Предыдущее сообщение | Следующее сообщение
From : ???
To : All
Subj : Multithreading (XV)

A comparison between OS/2 and Linux thread programming



This article covers mapping of threading and synchronization primitives.

Threads
A thread is the basic unit of execution in OS/2. Threads are the dispatchable units within an OS/2 process. Thread scheduling and
priority is handled entirely in the kernel.

The Linux kernel uses a process model rather than a threading model. However, the Linux kernel provides a lightweight process framework for creating threads,
and the actual thread implementation is in the user space. Currently there are various threading libraries available (LinuxThreads, NGPT, NPTL, and so on).
Research for this document was done using the LinuxThreads library, but the information herein is also applicable to Red Hat's Native POSIX Threading Library
(NPTL)

This section describes threading in OS/2 and in Linux. It covers the calls for creating threads, setting its attributes, and changing its priority.

The thread mapping table is as follows:

Table 1. Thread mapping table
OS/2
Linux
Classification
_beginthread
pthread_create
pthread_attr_init
pthread_attr_setstacksize
pthread_attr_destroy
mappable
_endthread
pthread_exit
mappable
DosWaitThread
pthread_join
pthread_attr_setdetachstate
pthread_detach
mappable
DosSetPriority
Processes
setpriority
sched_setscheduler
sched_setparam
Threads
pthread_setschedparam
pthread_setschedpolicy
pthread_attr_setschedparam
pthread_attr_setschedpolicy
context-specific


The Classification column indicates whether the OS/2 construct is mappable or context-specific:

Mappable: The OS/2 construct can be mapped to the specified Linux construct(s) by closely examining the types, parameters, return codes, and the like.
Both the OS/2 and Linux constructs provide similar functionality
Context-specific: The given OS/2 construct may or may not have an equivalent construct in Linux OR Linux may have more than one construct which
provides similar functionality. The decision to use a specific Linux construct(s) then depends on the application context.

Thread creation
The system call _beginthread is used for spawning threads in OS/2:

int _beginthread(void (*start_address) (void *), (void *) stack,
unsigned stack_size, void *arg);

Linux uses the pthread library call pthread_create() to spawn a thread:

int pthread_create (pthread_t *thread_id, pthread_attr_t *threadAttr,
void * (*start_address)(void *), void * arg);

Specifying the thread function
The parameter start_address for the OS/2 system call _beginthread is the address of the function
that the newly created thread will execute. The thread function must be declared and compiled using
_Optlink linkage.

The parameter start_address for the Linux library call pthread_create() is the address of the
function that the newly created thread will execute.

Parameter passing to the thread function
In OS/2, the parameter arg for the system call _beginthread() is used to specify the parameter to be
passed to the newly created thread. It specifies the address of the data item to be passed to the new thread.

In Linux, the parameter arg for the library call pthread_create() is used to specify the parameter to be
passed to the new thread

Setting the stack size
The parameter stack_size for the OS/2 system call _beginthread() is the size of stack, in bytes, that is
to be allocated for the new thread. The stack size should be a non-zero multiple of 4K and a minimum of 8K.

In Linux, the stack size is set in the pthread attributes object; that is, the parameter threadAttr of type
pthread_attr_t is passed to the library call pthread_create(). This object needs to be initialized
by the call pthread_attr_init() before setting any attributes. The attribute object is destroyed using the
call pthread_attr_destroy():

int pthread_attr_init(pthread_attr_t *threadAttr);
int pthread_attr_destroy(pthread_attr_t *threadAttr);

Note that all of the pthread_attr_setxxxx calls achieve similar functionality to the pthread_xxxx
calls (if available) except that pthread_attr_xxxx can only be used before thread creation to update the attribute object that will be passed as a parameter to
pthread_create. Meanwhile, pthread_xxxx calls can be used anytime after the thread has been created

The stack size is set using the call pthread_attr_setstacksize():

int pthread_attr_setstacksize(pthread_attr_t *threadAttr, int
stack_size);

Thread states
In OS/2 there are no explicit thread states maintained with respect to thread termination. However, it is possible to use DosWaitThread() call, which allows a
thread to wait explicitly on the termination of a specific or non-specific thread within the process.

In Linux, threads are by default created in joinable state. In joinable state, another thread can synchronize on the thread's termination and recover its termination
code using the function pthread_join(). The thread resources of the joinable thread are released only after it is joined.

OS/2 uses DosWaitThread() to wait for a thread to terminate:

APIRET DosWaitThread(PTID ptid, ULONG option);

Linux uses pthread_join to do the same:

int pthread_join(pthread_t *thread, void **thread_return);

In the detached state, the thread resources are immediately freed when it terminates. The detached state can be set by calling
pthread_attr_setdetachstate() on the thread attribute object.

int pthread_attr_setdetachstate (pthread_attr_t *attr, int
detachstate);

A thread created in a joinable state can later be put into a detached state using the pthread_detach() call:

int pthread_detach (pthread_t id);

Thread exit
In OS/2, the system call _endthread() is used terminate the thread.

The Linux equivalent for this is the library call pthread_exit(). The retval is the return value of the thread and it can be retrieved from another thread by
calling pthread_join():

int pthread_exit(void* retval);

Changing priority
OS/2 uses the system call DosSetPriority() to change the priority of a process or thread of a running process:

int DosSetPriority(int scope, int class, int delta, int id);

In this example, scope is PRTYS_PROCESS for a process; PRTYS_THREAD for a thread level is the priority level. The different priority levels allowed are:

No change : PRTYC_NOCHANGE
Idle-time : PRTYC_IDLETIME
Regular : PRTYC_REGULAR
Time-critical : PRTYC_TIMECRITICAL
Fixed high : PRTYC_FOREGROUNDSERVER

Where delta is the priority change to be applied to the priority level of the process. This value must range from -31 to +31. The higher the value, the higher the
priority. The current process or thread has an id of 0.

Linux provides a variety of calls to modify or change thread priority. Different calls should be used depending on the application context.

Normal or regular processes/threads
The Linux system call setpriority() is used to set or modify priority levels for normal processes and threads. The parameter scope is PRIO_PROCESS. Set
id to 0 to change the current process (or thread) priority. Again, delta is the priority value -- this time, in the range -20 to 20. Note also that in Linux, a lower delta
value means a higher priority. So you set +20 for IDLETIME priority and 0 for REGULAR priority.

In OS/2 the priority range is from 0 (lower priority) to 31 (higher priority). But in Linux the priority range for normal non-realtime processes is from -20 (higher)
to +20 (lower priority). This has to be mapped before being used.

int setpriority(int scope, int id, int delta);

Time-critical and realtime processes and threads
The Linux system call sched_setscheduler can be used to change the scheduling priority and policy of a running process:

int sched_setscheduler(pit_t pid, int policy, const struct sched_param *param);

The parameter "policy" is the scheduling policy. The possible values for policy are SCHED_OTHER (for regular non-realtime scheduling), SCHED_RR (realtime
round-robin policy), and SCHED_FIFO (realtime FIFO policy).


struct sched_param {
...
int sched_priority;
...
};



Here, param is a pointer to a structure representing scheduling priority. It can range from 1 to 99 only for realtime policies. For others (normal non-realtime
processes) it is zero.

In Linux, for a known scheduling policy, it is also possible to change only the process priority by using the system call sched_setparam:

int sched_setparam(pit_t pid, const struct sched_param *param);

The LinuxThreads library call pthread_setschedparam is the thread version of sched_setscheduler and is used to dynamically change the
scheduling priority and policy for a running thread:

int pthread_setschedparam(pthread_t target_thread, int policy,
const struct sched_param *param);

The parameter target_thread indicates the thread whose priority is to be changed and param indicates the priority.

The LinuxThreads library calls pthread_attr_setschedpolicy and pthread_attr_setschedparam can be used to set the scheduling policy
and the priority level to the thread attribute object before the thread is created:

int pthread_attr_setschedpolicy(pthread attr_t *threadAttr, int policy);

int pthread_attr_setschedparam(pthread attr_t *threadAttr, const
struct sched_param *param);

Let's see an example that highlights the OS/2 to Linux mapping with respect to thread creation and priority changes.

Thread examples
In this OS/2 example, thread1 is a regular thread, thread2 is a time-critical realtime thread:

Listing 1. OS/2 thread code

main () {
enum {StackSize = 120*1024}; /* stack size set to 120 K */
/* create a thread */
int thread1 = _beginThread (RegularThread, NULL, StackSize,
NULL);
int thread2 = _beginThread (CriticalThread, NULL,
StackSize, NULL);
}

/* Normal /Regular Priority Thread */
static void RegularThread (void *) {
/* Set the priority of the thread. 0 is passed as an
argument to identify the current thread */
int iRc = DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, 20,
0);
_endThread();
}

/* Realtime time critical Priority Thread */
static void CriticalThread (void *) {
int iRc = DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL,
20, 0);
_endThread();
}



The Linux equivalent for the above OS/2 code:

Listing 2. Equivalent Linux thread code

#define STATIC 0
static void * RegularThread (void *);
static void * CriticalThread (void *);
/* Regular non-realtime Thread function */
static void * RegularThread (void *d)
{
int priority = 10; //0 for Regular, +20 for Idletime
/* Set the priority - normal non-realtime priority */
int irc = setpriority(PRIO_PROCESS, 0, priority);
pthread_exit(NULL);
}
/* Time Critical Realtime Thread function */
static void * CriticalThread (void *d)
{
if (STATIC == 0) {
/* change the thread priority dynamically */
struct sched_param param; // scheduling priority
int policy = SCHED_RR; // scheduling policy
/* Get the current thread id */
pthread_t thread_id = pthread_self();
/* To set the scheduling priority of the thread */
param.sched_priority = 99;
int irc = pthread_setschedparam(thread_id, policy, ¶m);
}
pthread_exit(NULL);
}
int main (void)
{
pthread_t thread1, thread2; // thread identifiers
pthread_attr_t threadAttr1, threadAttr2; // thread attributes
struct sched_param param; // scheduling priority
int policy = SCHED_RR; // scheduling policy - real time
int irc, rc;
rc = pthread_attr_init(&threadAttr1); /* init the attr 1*/
rc = pthread_attr_init(&threadAttr2); /* init the attr 2*/
/* Set the stack size of the thread */
irc = pthread_attr_setstacksize(&threadAttr1, 120*1024);
irc = pthread_attr_setstacksize(&threadAttr2, 120*1024);
/* Set thread to detached state. No need for pthread_join*/
irc = pthread_attr_setdetachstate(&threadAttr1,
PTHREAD_CREATE_DETACHED);
irc = pthread_attr_setdetachstate(&threadAttr2,
PTHREAD_CREATE_DETACHED);
if (STATIC == 1) {
/* priority is set statically */
/* Set the policy of the thread to real time*/
irc = pthread_attr_setschedpolicy(&threadAttr2, policy);
/* Set the scheduling priority of the thread - max
priority*/
param.sched_priority = 99;
irc = pthread_attr_setschedparam(&threadAttr2, ¶m);
}
/* Create the threads */
irc = pthread_create(&thread1, &threadAttr1, RegularThread, NULL);
irc = pthread_create(&thread2, &threadAttr2, CriticalThread,
NULL);
/* Destroy the thread attributes */
irc = pthread_attr_destroy(&threadAttr1);
irc = pthread_attr_destroy(&threadAttr2);
}



Mutexes
Various points need to be considered in the mapping process of a mutex, including:

Type of mutex: OS/2 mutex semaphores are recursive by default whereas this is not the case in Linux.
Initial state: A mutex in OS/2 can be owned on creation whereas this support is not available in Linux. To achieve the same in Linux, the mutex should
be locked explicitly after creation.
Semaphores: OS/2 supports named and un-named semaphores whereas the current implementation of Linux pthreads does not support this option.
Timeout: In OS/2, timeout can be specified while acquiring the mutex. In Linux this option is not available (a workaround follows later in this article).

The mutex mapping table is as follows:

Table 2. Mutex mapping table
OS/2
Linux
Classification
DosCreateMutexSem
DosRequestMutexSem
DosReleaseMutexSem
DosCloseMutexSem
pthread_mutex_init
pthread_mutex_lock/pthread_mutex_trylock
pthread_mutex_unlock
pthread_mutex_destroy
Mappable


Creating a mutex
In OS/2, the system call DosCreateMutexSem() is used to create the Mutex:

APIRET DosCreateMutexSem (PSZ pszName, PHMTX phmtx, ULONG flAttr, BOOL32 fState);

The parameter pszName takes the ASCII name of the mutex.

The pthread library call pthread_mutex_init() is used to create the mutex in Linux:

int pthread_mutex_init(pthread_mutex_t *mutex, const
pthread_mutexattr_t *mutexattr);

There are three "kinds" of mutex in Linux. The mutex "kind" determines what happens if a thread attempts to lock a mutex it already owns with
pthread_mutex_lock:

Fast mutex: While trying to lock the mutex using pthread_mutex_lock() the calling thread suspends forever.
Recursive mutex:pthread_mutex_lock() returns immediately with a success return code.
Error check mutex:pthread_mutex_lock() returns immediately with the error code EDEADLK.

The mutex "kind" can be set in two ways. The static way of setting is as follows:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/* For Fast mutexes */
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
/* For recursive mutexes */
pthread_mutex_t errchkmutex =
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;/* For errorcheck mutexes */

Another way of setting mutex "kind" is by using a mutex attribute object. To do this, pthread_mutexattr_init() is called to initialize the object
followed by a pthread_mutexattr_settype(), which sets the "kind" of the mutex:

int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);

The parameter "kind" takes the following values:

PTHREAD_MUTEX_FAST_NP
PTHREAD_MUTEX_RECURSIVE_NP
PTHREAD_MUTEX_ERRORCHECK_NP

The attribute can be destroyed using pthread_mutexattr_destroy():

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

Locking or acquiring a mutex
OS/2 uses DosRequestMutexSem() to lock (acquire) the mutex:

APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout);

The parameter ulTimeOut is used to specify the maximum amount of time the thread is to be blocked:

On Linux, the pthread library calls pthread_mutex_lock() and pthread_mutex_trylock() are used to acquire a mutex:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);

The first of these, pthread_mutex_lock(), is a blocking call -- which means that if the mutex is already locked by another thread,
pthread_mutex_lock() suspends the calling thread until the mutex is unlocked.

On the other hand, pthread_mutex_trylock() returns immediately if the mutex is already locked by another thread.

Note that in Linux, the timeout option is not available. This same effect can be achieved by issuing a non-blocking pthread_mutex_trylock() call along
with a delay in a loop, which counts the timeout value. (Refer to Listing 5 for sample code).

Unlocking or releasing a mutex
OS/2 uses DosReleaseMutexSem() to relinquish ownership of a mutex semaphore:

APIRET DosReleaseMutexSem(HMTX hmtx);

Linux uses pthread_mutex_unlock() to release (unlock) the mutex:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

Note that mutex functions are not async-signal safe and should not be called from a signal handler. In particular, calling pthread_mutex_lock or
pthread_mutex_unlock from a signal handler may deadlock the calling thread.

Destroying a mutex
In OS/2, DosCloseMutexSem() is used to close a mutex semaphore:

APIRET DosCloseMutexSem (HMTX hmtxSem);

On Linux, pthread_mutex_destroy() destroys a mutex object, freeing the resources it might hold. It also checks whether the mutex is unlocked at that
time:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Setting state
The parameter fState of the DosCreateMutexSem() is used to specify the initial state of the mutex in OS/2. fState can take two possible values:

value 1 creates a mutex with initially owned state
value 0 creates a mutex with initially un-owned state

In Linux, the initial state of the mutex cannot be set using the pthread_mutex_init() call. But it can be achieved by following steps:

1.Create a mutex using pthread_mutex_init()
2.Lock (acquire) the mutex using pthread_mutex_lock()

Mutex examples

Listing 3. OS/2 mutex code

hmtx hmtxSem; // semaphore handle
unsigned long ulRc; // return code

/* Create a un named mutex semaphore */
ulRc = DosCreateMutexSem (NULL, &hmtxSem, 0, FALSE);

ulRc = DosRequestMutexSem (hmtxSem, (unsigned long)
SEM_INDEFINITE_WAIT);

/* Access the shared resource */
...

/* Release the mutex */
ulRc = DosReleaseMutexSem(hmtxSem);
...

/* Closes the semaphore */
ulrc = DosCloseMutexSem (hmtxSem);



Compare this to the following Linux code:

Listing 4. Linux mutex code

/* Declare the mutex */
pthread_mutex_t mutex;
/* Attribute for the mutex */
pthread_mutexattr_t mutexattr;
/* Set the mutex as a recursive mutex */
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE_NP);

/* create the mutex with the attributes set */
pthread_mutex_init(&mutex, &mutexattr);

/* lock the mutex */
pthread_mutex_lock (&mutex);

/* access the shared resource */
..

/* unlock the mutex */
pthread_mutex_unlock (&mutex);
...

/* destroy the attribute */
pthread_mutexattr_destroy(&mutexattr)
/* Close/destroy the semaphore */
irc = pthread_mutex_destroy (&mutex);



The next example illustrates how to simulate the timeout option while trying to aquire a mutex:

Listing 5. Simulating a timeout in Linux

#define TIMEOUT 100 /* 1 sec */
struct timespec delay;
/* Declare the mutex */
pthread_mutex_t mutex;

while (timeout < TIMEOUT ) {
delay.tv_sec = 0;
delay.tv_nsec = 1000000; /* 1 milli sec */

irc = pthread_mutex_trylock(&mutex);
if (!irc) {
/* we now own the mutex */
break;
}
else {
/* check whether somebody else has the mutex */
if (irc == EPERM ) {
/* sleep for delay time */
nanosleep(&delay, NULL);
timeout++ ;
}
else{
/* error */
}
}
}



Semaphores
Linux provides POSIX semaphores as well as pthread conditional variables to map OS/2 event semaphore constructs (Linux also provides SVR-compliant semop,
semctl, and similar calls, but for the purposes of this document we are limiting ourselves to the POSIX and LinuxThreads implementations). Both have their share
of pros and cons. It is up to the user's discretion to use either of them based on application logic. The various points that needs to be considered in the mapping
process of the event semaphore are:

Type of semaphore: OS/2 supports both named and un-named event semaphores and the named semaphores are shared across processes. Linux does
not support this option.
Initial state: In OS/2, the semaphore can have an initial value. In Linux, POSIX semaphore supports this functionality but pthreads does not. This needs
to be considered when using pthreads.
Timeout: OS/2 event semaphores supports timed wait. In Linux the POSIX semaphore implementation supports only indefinite wait (blocking). The
pthreads implementation supports both blocking as well as timeouts. The pthread_cond_timedwait() call provides a timeout value during wait
and pthread_cond_wait() is used for indefinite wait.
Signaling: In OS/2, signaling a semaphore wakes up all the threads that are waiting on the semaphore. In Linux, the POSIX thread implementation
wakes up only one thread at a time while the pthreads implementation has a pthread_cond_signal() that wakes up one thread and a
pthread_cond_broadcast() call that signals all the threads waiting on the semaphore.
Synchronicity: An event semaphore is asynchronous in OS/2. In Linux, a POSIX semaphore is asynchronous whereas a pthreads conditional variable is
synchronous.

To sum it up, POSIX semaphores can be considered when no timed waits or broadcast wakeup are required. If the application logic requires broadcast wakeups
and timeouts, pthread conditional variable can be used.

The Semaphore mapping table is as below:

Table 3. Semaphore mapping table
OS/2
POSIX Linux
calls
pthread Linux calls
Classification
DosCreateEventSem
sem_init
pthread_cond_init
context-specific
DosPostEventSem
sem_post
pthread_cond_signal /
pthread_cond_broadcast
context-specific
DosWaitEventSem
sem_wait/sem_trywait
pthread_cond_wait /
pthread_cond_timedwait
context-specific
DosCloseEventSem
sem_destroy
pthread_cond_destroy
context-specific


Creating an event semaphore
OS/2 uses DosCreateEventSem() call to create an event semaphore:

APIRET DosCreateEventSem (PSZ pszName,PHEV phev, ULONG flAttr, BOOL32 fState);

Here, pszName is a pointer to ASCII name of the semaphore. If this parameter is NULL, DosCreateEventSem() creates a un-named event semaphore.
fState is used to set the state of the event semaphore. If it is 0, the semaphore is initially reset, and if it is 1, the semaphore is initially posted.

In Linux, the call sem_init() creates a POSIX semaphore:

int sem_init(sem_t *sem, int pshared, unsigned int value);

Where value (semaphore count) is set to the initial value of the semaphore.

Linux pthreads uses pthread_cond_init() to create a conditional variable:

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

Conditional variables of type pthread_cond_t can be initialized statically, using the constant PTHREAD_COND_INITIALIZER. They can also be
initialized using pthread_condattr_init(), which initializes the attributes associated with the conditional variable. The call
pthread_condattr_destroy() is used to destroy the attributes:

int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);

Waiting on an event semaphore
In OS/2, DosWaitEventSem() is used to block a thread and wait on the event semaphore:

APIRET DosWaitEventSem (HEV hev, ULONG ulTimeOut);

Where the parameter ulTimeOut specifies the timeout value. If the semaphore is not posted within the specified time, the DosWaitEventSem() call will
return with an error code. If -1 is specified as timeout value, it blocks the calling thread indefinitely.

Linux POSIX semaphores use sem_wait() to suspend the calling thread until the semaphore has a non-zero count. It then atomically decreases the semaphore
count:

int sem_wait(sem_t * sem)

The timeout option is not available in POSIX semaphore. This can be achieved by issuing non-blocking sem_trywait() within a loop, which counts the
timeout value:

int sem_trywait(sem_t * sem);

Linux pthreads uses pthread_cond_wait() to block the calling thread indefinitely:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

On the other hand, if the calling thread needs to be blocked for a specific time, then pthread_cond_timedwait() is used to block the thread. If the
conditional variable is not posted within the specified time, pthread_cond_timedwait() returns with an error:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t
*mutex,const struct timespec *abstime);

Here, the abstime parameter specifies an absolute time (specifically, the time elapsed since 00:00:00 GMT, January 1, 1970.)

Signaling an event semaphore
OS/2 uses DosPostEventSem() to post an event semaphore. This wakes up all the threads that are waiting on the semaphore:

APIRET DosPostEventSem (HEV hev);

Linux POSIX semaphores use sem_post() to post an event semaphore. This wakes up any one of the threads blocked on the semaphore:

int sem_post(sem_t * sem);

The call pthread_cond_signal() is used in LinuxThreads to wake up a thread waiting on the conditional variable, while
pthread_cond_broadcast() is used to wake up all the threads that are waiting on the conditional variable.

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

Note that condition functions are not async-signal safe, and should not be called from a signal handler. In particular, calling pthread_cond_signal or
pthread_cond_broadcast from a signal handler may deadlock the calling thread.

Destroying an event semaphore
OS/2 uses DosCloseEventSem() to destroy the event semaphore:

APIRET DosCloseEventSem (HEV hev);

Linux POSIX semaphores use sem_destroy() to destroy the semaphore:

int sem_destroy(sem_t * sem);

In Linux pthreads, pthread_cond_destroy() is used to destroy the conditional variable:

int pthread_cond_destroy(pthread_cond_t *cond);

Event semaphore examples

Listing 6. OS/2 semaphore code

HEV hevIpcInterrupt;
unsigned long ulPostCnt = 0;
unsigned long ulrc; // return code
unsigned long ulTimeout = 10 ; // timeout value

/* create event semaphore */
DosCreateEventSem (NULL, &hevIpcInterrupt, 0, TRUE);

/* In Thread A */
/* Wait forever for event to be posted */
DosWaitEventSem (hevIpcInterrupt, (unsigned long)
SEM_INDEFINITE_WAIT);
/* immediately unblocked as the semaphore is already posted */

/* Waits until the semaphore is posted */
DosWaitEventSem (hevIpcInterrupt, (unsigned long)
SEM_INDEFINITE_WAIT);

/* In Thread B */
DosPostEventSem(hevIpcInterrupt);

/* Close the semaphore */
ulrc = DosCloseEventSem (hevIpcInterrupt);



The following Linux example uses pthread conditional variable for synchronization between two threads, A and B:

Listing 7. Linux condition variable

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;

struct timeval tvTimeNow; //Absolute current time
struct timezone tzTimeZone; //Timezone
struct timespec tsTimeOut; // Input for timedwait

/* In Thread A */
...
pthread_mutex_lock(&mutex);
/* signal one thread to wake up */
pthread_cond_signal(&condvar);
pthread_mutex_unlock(&mutex);
/* this signal is lost as no one is waiting */


/* In Thread B */

pthread_mutex_lock(&mutex);

pthread_cond_wait(&condvar, &mutex);

pthread_mutex_unlock(&mutex);


/* Thread B blocks indefintely */

/* ---------------------------------------------------------------------
--------------------------------------------------------------------- */

/* One way of avoiding losing the signal is as follows */


/* In Thread B - Lock the mutex early to avoid losing signal
*/



pthread_mutex_lock (&mutex);



/* Do work */

.......

/* This work may lead other threads to send signal to
thread B */

/* Thread A now tries to take the mutex lock
to send the signal but gets blocked */
...
pthread_mutex_lock(&mutex);


/* Thread B: Get the current time */

gettimeofday (&tvTimeNow, &tzTimeZone);

/* Calculate the absolute end time - 10 seconds wait */

tsTimeOut.tv_sec = tvTimeNow.tv_sec + 10 ;

/* Thread B waits for the specified time for the signal to be
posted */

pthread_cond_timedwait (&condvar, &mutex, &tsTimeOut );
/* Thread A now gets the lock and can
signal thread B to wake up */
pthread_cond_signal(&condvar */
pthread_mutex_unlock(&mutex);
... /* Thread B
unblocks upon receipt of signal */

pthread_mutex_unlock (&mutex);



The next example uses POSIX semaphores to implement synchronization between threads A and B:

Listing 8. Linux POSIX semaphore

sem_t sem; /* semaphore object */
int irc; /* return code */

/* Initialize the semaphore - count is set to 1*/
irc = sem_init (sem, 0,1);
...

/* In Thread A */

/* Wait for event to be posted */

sem_wait (&sem);

/* Unblocks immediately as semaphore initial count was set to 1 */

.......

/* Wait again for event to be posted */

sem_wait (&sem);

/* Blocks till event is posted */

/* In Thread B */
/* Post the semaphore */
...
irc = sem_post (&sem);

/* Destroy the semaphore */
irc = sem_destroy(&sem);






Mon 09 Feb 2004 16:29 Mozilla/4.61 [en] (OS/2; U)




Programmed by Dmitri Maximovich, Dmitry I. Platonoff, Eugen Kuleshov.
25.09.99 (c) 1999, RU/2. All rights reserved.
Rewritten by Dmitry Ban. All rights ignored.