This article covers mapping of threading and synchronization primitives. Subsequent articles in this series will cover system calls, networking calls, and other programming constructs.
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 article was done using the LinuxThreads library, but the information here 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 a thread, setting its attributes, and changing its priority. The thread mapping table is as follows:
| OS/2 | Linux | Classification |
_beginthread | pthread_createpthread_attr_initpthread_attr_setstacksizepthread_attr_destroy | Mappable |
_endthread | pthread_exit | Mappable |
DosWaitThread | pthread_joinpthread_attr_setdetachstatepthread_detach | Mappable |
DosSetPriority | Processes setprioritysched_setschedulersched_setparamThreads pthread_setschedparampthread_setschedpolicypthread_attr_setschedparampthread_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 that provides similar functionality. In either case, the decision to use a specific Linux construct(s) depends on the application context.
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
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);
In OS/2 there are no explicit thread states maintained with respect to
thread termination. However, it is possible to use the 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);
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);
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).
Listing 1. Changing the scheduling priority
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);
Here's an example that highlights the OS/2-to-Linux mapping with respect to thread creation and priority changes.
In this OS/2 example, thread1 is a regular thread, and thread2 is a time-critical realtime thread:
Listing 2. 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();
}
|
Here is the Linux equivalent for the above OS/2 code:
Listing 3. 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);
}
|
In the mapping of a mutex, consider these points:
- 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:
| OS/2 | Linux | Classification |
DosCreateMutexSemDosRequestMutexSemDosReleaseMutexSemDosCloseMutexSem | pthread_mutex_initpthread_mutex_lock/pthread_mutex_trylockpthread_mutex_unlockpthread_mutex_destroy | Mappable |
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_NPPTHREAD_MUTEX_RECURSIVE_NPPTHREAD_MUTEX_ERRORCHECK_NP
The attribute can be destroyed using pthread_mutexattr_destroy():
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
OS/2 uses DosRequestMutexSem() to lock (acquire) the mutex:
APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout);
The parameter ulTimeOut specifies 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 6 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.
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);
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:
- Create a mutex using
pthread_mutex_init(). - Lock (acquire) the mutex using
pthread_mutex_lock().
Listing 4. 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 5. 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 acquire a mutex:
Listing 6. 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 */
}
}
}
|
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 article we are limiting ourselves to the POSIX and LinuxThreads implementations). Both have their share of pros and cons. It is up to your discretion to use either of them based on application logic. The various points to consider 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 andpthread_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 apthread_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 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.
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 |
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);
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.)
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.
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);
Listing 7. 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 8. 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 indefinitely */
/* ---------------------------------------------------------------------
--------------------------------------------------------------------- */
/* 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 listing uses POSIX semaphores to implement synchronization between threads A and B:
Listing 9. 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);
|
This article covered the mapping of OS/2 to Linux with respect to threads, mutexes, and event semaphores. You can use this information as a reference for undertaking any migration activity involving OS/2 to Linux. The tips and caveats should help to simplify your migration design; you can refer to the relevant man pages for more detailed information on all of the Linux calls mentioned in this article. The next article in this series will cover the mapping of system calls related to memory management, file handling, and device driver interfaces.
- Be sure to read the next two installments in the series, "Migrate your apps from OS/2 to Linux: Part 2. Memory management, IPC, and file handling" (developerWorks, May 2004), and "Migrate your apps from OS/2 to Linux: Part 3. Timer and DLL calls" (developerWorks, August 2004).
- The Design of OS/2 by H.M. Deitel and M.S. Kogan
(Addison-Wesley, 1992) can still be found at used book sites; the ISBN is
0-201-54889-5.
-
See the online code examples for the book Pthreads
Programming by Bradford Nichols, Dick Buttlar, and Jacqueline Proulx
Farrel (O'Reilly & Associates, 1996).
-
Don't forget to check the Linux "info" and man pages for specific
calls for more details on
programming with threads in Linux.
-
Two previous IBM developerWorks Linux articles covered threads
programming: Peter Seebach's "Basic use of pthreads" (developerWorks, January 2004) and Daniel Robbin's "POSIX
threads explained (developerWorks, July 2000)."
- IBM's OS/2 Strategy for
2004 recommends a phased transition to the WebSphere software
platform.
-
Most of the
IBM eServer
servers now come bundled with Linux.
- IBM Redbooks offers a OS/2
Server Transition for OS/2 administrators who are planning
a transition to Windows or to Linux.
- See Migrate / Port your application to Linux with IBM for additional information.
- Porting
OS/2 applications to Linux (in C) (developerWorks, March 2002) covers issues encountered by the LAN Distributed Platform (LANDP)
for Linux team while porting LANDP from OS/2 to Linux.
- You can also use OS/2 and Linux together on the same machine. Check
out How to
use Linux and OS/2 Together for a list of resources from the Southern
California OS/2 User Group, and the SUSE LINUX portal's OS/2 and
Linux with the OS/2 Boot Manager for easy directions on dual-booting.
- Find more resources for Linux developers in the IBM developerWorks Linux zone.
- Browse for books on these and other technical topics.
Dinakar Guniguntala holds a Bachelor of Engineering degree in Computer Engineering from REC Surat. He has been employed with IBM Global Services, India, since February 1998, where he works on operating system internals. He has worked on the OS/2 kernel, graphics engine, and filesystems, as well as on embedded Linux, the Linux kernel, and Linux thread libraries. He can be reached at dgunigun-at-in.ibm.com.
Sathyan Doraiswamy holds a Bachelor of Engineering degree in Electronics and Communications from University Visvesvaraya College of Engineering, Bangalore. He joined IBM India in June 1997. He works for Engineering & Technology Services and specializes in firmware for embedded and real time systems. He has vast programming experience on Windows and Linux device drivers and on VxWorks. You can contact him at dsathyan-at-in.ibm.com.
Anand K. Santhanam has a Bachelor of Engineering degree in Computer Science from Madras University, India. He has been in IBM Global Services (Software Labs), India, since July 1999. He is a member of the Linux Group at IBM, where he has worked with ARM-Linux, character/X-based device drivers, power management in embedded systems, PCI device drivers, and multithreaded programming in Linux. Other areas of interest are OS internals and networking. You can contact him at asanthan-at-in.ibm.com.
Srinivasan S. Muthuswamy has a Bachelor of Engineering degree in Computer Engineering from the Government College of Technology, Coimbatore, India. He has been in IBM Global Services, India since August 2000. He has worked on multithreaded applications in Linux. He has worked on Web applications using WSBCC/Java technologies/WebSphere/MQSeries. He has also worked on Crystal Reports and Lotus Domino. You can contact him at smuthusw-at-in.ibm.com.
Rinson Antony has a Bachelor of Engineering degree in Computer Engineering from Bombay University, India. He has been in IBM Global Services, India since July 2000. He has worked on multithreaded applications in Linux and Web applications using Java technologies/WebSphere/XML. Other areas of interest are PCI device drivers and networking. You can contact him at arinson-at-in.ibm.com.
Brahmaiah Vallabhaneni has a Bachelor of Engineering degree from BITS, Pilani, India. He has been in IBM Global Services, India, since August 2002. He has worked on GNU C Compiler porting, character device drivers, power management in embedded systems, PCI device drivers, and multithreaded programming in Linux. Other areas of interest are Linux internals and networking. You can contact him at bvallabh-at-in.ibm.com.
Comments (Undergoing maintenance)





