Skip to main content

Migrate your apps from OS/2 to Linux: Part 1. Threads, mutexes, and semaphores

POSIX, OS/2, and how to bridge them

Dinakar Guniguntala (mut-at-in.ibm.com), Staff Software Engineer, IBM, Software Group
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 (dsathyan-at-in.ibm.com), Staff Software Engineer, IBM, Software Group
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 Santhanam (asanthan-at-in.ibm.com), Software Engineer, IBM, Software Group
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 Muthuswamy (smuthusw-at-in.ibm.com), Software Engineer, IBM, Software Group
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 (arinson-at-in.ibm.com), Software Engineer, IBM, Software Group
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 (bvallabh-at-in.ibm.com), Software Engineer, IBM, Software Group
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.

Summary:  Linux is evolving as the predominant operating system of the new millennium, and legacy OSes such as OS/2® are being gradually phased out. This series of articles helps the developers involved in the tedious process of migrating/porting the OS/2 system drivers and applications to Linux. It provides a one-to-one mapping of various OS/2-to-Linux calls related to threads, IPC, memory management, timer handling, file handling, and so on. In addition, it captures the various preprocessor directives and compiler/linker options that can be mapped from OS/2 to Linux. This is the first in a series of three articles.

Date:  05 Feb 2004
Level:  Advanced
Activity:  2248 views

This article covers mapping of threading and synchronization primitives. Subsequent articles in this series will cover system calls, networking calls, and other programming constructs.

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 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:

Table 1. Thread mapping table

OS/2LinuxClassification
_beginthreadpthread_create
pthread_attr_init
pthread_attr_setstacksize
pthread_attr_destroy
Mappable
_endthreadpthread_exitMappable
DosWaitThreadpthread_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 that provides similar functionality. In either case, the decision to use a specific Linux construct(s) depends on the application context.

Getting started with Linux

Linux has been growing in popularity since its inception in 1991. One of the major strengths of Linux is that it is open source, free software (for more on what that means, please see the Resources listed at the end of this sidebar).

As Linux celebrates its latest major release (of the 2.6 kernel), more and more companies are shifting their business processes towards open standards, which includes releasing products based on Linux.

Among large firms, IBM® in particular is actively involved with the open source community. Particular areas of interest for IBM are improving Linux scalability and stability, with a focus on enterprise computing. Most of the IBM eServer™ servers now come bundled with Linux.

As a part of its Linux initiative, IBM is actively involved in several projects to migrate products based on legacy operating systems such as OS/2 onto Linux.

  • The Free software foundation has information on what Free software is all about.
  • Read the Open source definition to better understand the difference between free and open source software.
  • IBM developerWorks has a page devoted to resources and information for those who are New to Linux.
  • Most of the IBM eServer servers now come bundled with Linux.

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 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);

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).


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.

Thread examples

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, &param);
       }
       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, &param);
        }
       /* 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

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:

Table 2. Mutex mapping table

OS/2LinuxClassification
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 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.

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 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  */
           }
       }
   }


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 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 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 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/2POSIX Linux callspthread Linux callsClassification
DosCreateEventSemsem_initpthread_cond_initContext-specific
DosPostEventSemsem_postpthread_cond_signal / pthread_cond_broadcastContext-specific
DosWaitEventSemsem_wait/sem_trywaitpthread_cond_wait / pthread_cond_timedwait Context-specific
DosCloseEventSemsem_destroypthread_cond_destroyContext-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 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);


Conclusion

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.


Resources

About the authors

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)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=11378
ArticleTitle=Migrate your apps from OS/2 to Linux: Part 1. Threads, mutexes, and semaphores
publish-date=02052004
author1-email=mut-at-in.ibm.com
author1-email-cc=
author2-email=dsathyan-at-in.ibm.com
author2-email-cc=tomyoung@us.ibm.com
author3-email=asanthan-at-in.ibm.com
author3-email-cc=
author4-email=smuthusw-at-in.ibm.com
author4-email-cc=
author5-email=arinson-at-in.ibm.com
author5-email-cc=
author6-email=bvallabh-at-in.ibm.com
author6-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers