TIP: Click on subject to list as thread! ANSI
echo: os2prog
to: James R. Cook
from: Peter Fitzsimmons
date: 1997-01-10 15:25:59
subject: DosRead blocking

JRC>  :)  I understand.  *sigh*  Is there an alternative to using Mutex
 JRC>  semaphores?  They seem to take a *LONG* time to lock/unlock (or is that
 JRC>  block/unblock).  I recall seeing some code that used 
 JRC> the DosEnterCritSec

Forget that function exists.

IBM's compiler has a great set of undocumented semaphore functions (or you
can roll your own,  but you _must_ use a little assembly language for the
LOCK prefix (to make it SMP safe)).

These type of semaphores use a local flag to keep a lock count (and this is
the flag that requires the intel LOCK prefix).  Only when the lock count is
greater than zero,  does the "fast semaphore" code call an OS/2
sem api to handle it (You want to call an OS/2 api here,  and not your own
"wait" function,  so that the os/2 schedular can do its magic).

Rolling your own code for this was easy under OS/2 1.x,  but got a little
complicated under OS/2 2.x (because you have to use two types of
semahpores).

The reasoning is:  Since a collision is rare,  especially when you only
                   have one cpu,  why call DosSem function every time?
                   Avoid the call by keeping a local flag indicating
                   whether or not a collision has taken place.

                   DosEnterCritSec() is the other extreme;  it assumes
                   every thread in your program has collided (asked for
                   access to the same resource) at the same time, even
                   if the other threads never use it!

                   A "fast semaphore" is faster than DosEnterCritSec()
                   anyway.

Here's an old note I wrote:

 Recently,  someone was inquiring about the "fast semaphores" that the
 IBM Cset++ runtime library uses internally.   These sems _are_ SMP
 safe. I was able to hack this (use at your own risk;  as long as you
 keep the Open/Request/Relase/Close paradigm,  you can easily write the
 code for the real OS/2 api's if things go wrong,  or the semaphore api
 for any OS;  think of this as the start of your portable semaphore API
 library).

  #include 
  #include 
  #include 
  #include 
  #include 
  #define INCL_NOPMAPI
  #define INCL_DOS
  #include 

  typedef struct _fastsem {
      long cnt;
      long hev;
      long hmtx;
  }FASTSEM;


  #if 1
      int _CreateSem(FASTSEM *, int StateOwned);
      int _RequestSem(FASTSEM *);
      int _ReleaseSem(FASTSEM *);
      int _CloseSem(FASTSEM *);
  #else   // use these to see it fail:
      #define _CreateSem(x, y) 0
      #define _RequestSem(x)   0
      #define _ReleaseSem(x)   0
      #define _CloseSem(x)     0
  #endif


  static FASTSEM gFSem;
  static volatile int flag = 0;


  void Thread2(void *ignore)
  {
      int rc;
      for(;;){
          rc = _RequestSem(&gFSem);
          assert(!flag);  // if flag is set,  there was a collision
          flag++;
          if(rc){
              printf("error %d from _ReQuestSem\n", rc);
              exit(1);
          }
          DosSleep(0L);
          flag--;
          rc = _ReleaseSem(&gFSem);
          if(rc){
              printf("error %d from _ReleaseSem\n", rc);
              exit(1);
          }
          printf("Thread %d had it\n", *_threadid);
      }
  }

  int main(void)
  {
      int rc;
      rc = _CreateSem(&gFSem, 0);
      if(rc){
          printf("error %d from _CreatSem\n", rc);
          exit(1);
      }
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      getchar();
  }

 Bench Test
 ==========

 A program was written to Request/Release a semaphore 500,000 times.  Three
 types of sems were tested:

     1) 16 Bit OS/2 1.x Ram Semaphores
     2) 32 bit anonymous mutex sempahorees
     3) The Cset++ 2.1 fast semaphores.

 Results (time in milliseconds; faster is better)
 ================================================

 16 bit (running under OS/2 1.x) : 6125
 16 bit (running under OS/2 2.x) : 3281
 32 bit mutex                    : 11937
 32 bit cset                     : 1000

 All of these sems are fast,  considering the number of iterations.


 For IBM VA C++ 3.0 -- not 100% confident
 ========================================

 Changes:  _CreateSem() has a new parameter (possibly a semaphore name,
           as there is a new function called _OpenSem() that takes a name).

 The "FASTSEM" structure has changed,  and is of unknown size -- it may
 even be smaller than before,  since a 'htmx' is no longer used.


  typedef struct _fastsem {
      char x[16];
  }FASTSEM;


  #if 1
      int _CreateSem(FASTSEM *, int unKnown, int StateOwned);
      int _RequestSem(FASTSEM *);
      int _ReleaseSem(FASTSEM *);
      int _CloseSem(FASTSEM *);
  #else   // use these to see it fail:
      #define _CreateSem(x, y, z) 0
      #define _RequestSem(x)   0
      #define _ReleaseSem(x)   0
      #define _CloseSem(x)     0
  #endif


  static FASTSEM gFSem;
  static volatile int flag = 0;


  void _Optlink Thread2(void *ignore)
  {
      int rc;
      for(;;){
          rc = _RequestSem(&gFSem);
          assert(!flag);  // if flag is set,  there was a collision
          flag++;
          if(rc){
              printf("error %d from _ReQuestSem\n", rc);
              exit(1);
          }
          DosSleep(0L);   // provide a window for failure.
          flag--;
          rc = _ReleaseSem(&gFSem);
          if(rc){
              printf("error %d from _ReleaseSem\n", rc);
              exit(1);
          }
          printf("Thread %d had it\n", *_threadid);
          DosSleep(0L);
      }
  }

  int main(void)
  {
      int rc;
      setvbuf(stdout, NULL, _IONBF, 0);
      rc = _CreateSem(&gFSem, 0, 1);
      if(rc){
          printf("error %d from _CreatSem\n", rc);
          exit(1);
      }
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      _beginthread(Thread2, NULL, 0x2000, NULL);
      printf("press enter to release sem from thread 1\n");
      getchar();
          rc = _ReleaseSem(&gFSem);
          if(rc){
              printf("error %d from _ReleaseSem\n", rc);
              exit(1);
          }
          printf("Thread %d had it\n", *_threadid);
      printf("press enter to end pgm\n");
      getchar();
  }

 --- Maximus/2 2.01
 SEEN-BY: 259/414
 ------------------------------------------------------------------------------

 Area 62, Msg#1470, Feb-16-96 09:39:46
    From: jeroenj{at}ibm.net
      To: All
 Subject: Re: Implementing Fast Ram Semaphores

 From: jeroenj{at}ibm.net
 Newsgroups: comp.os.os2.programmer.misc
 Subject: Re: Implementing Fast Ram Semaphores

 In , jimh{at}hilgraeve.com (Jim Harmer) writes:

 >A couple of weeks ago, I saw the __?xchg functions mentioned
 > [...]
 >create Fast Ram Semaphores, which could intern be used to

 > [...]
 > - If so how did you set it up?
 In pseudocode:
   volatile int isSet, sleeping;
   inline void lock () { while (xchg (&isSet, 1)) sleep (); }
   inline void unlock () { isSet = 0; if (sleeping) wakeup (); }
   void sleep () { DosResetEventSem (); sleeping = True; DosWaitEventSem (); }
   void wakeup () { sleeping = False; DosPostEventSem (); }
 In this scheme a thread will not obtain an OS/2 semaphore unless there is a
 chance
 that another thread owns the semaphore.

 > - We currently use Mutex Semaphores, do you think this
 >  would be faster or should we stick with the mux?
 Fast Ram semaphores are very much faster. We got an performance boost
 in the order of magnitude of 10-20% *only* by using these semaphores
 (our memory management has been made reentrant using these).

 Hope this helps.

 ------------------------------------------------------------------------------



--- Maximus/2 3.00
* Origin: Sol 3 * Toronto * V.32 * (905)858-8488 (1:259/414)
* Origin: Sol 3 * Toronto * V.32 * (905)858-8488 (1:259/414)
SEEN-BY: 50/99 54/99 270/101 620/243 625/0 160 711/401 409 410 413 430 808
SEEN-BY: 711/809 934 955 712/311 407 505 506 517 623 624 704 841 713/317
SEEN-BY: 800/1
@PATH: 259/414 99 2424/38 11 10 396/1 270/101 712/624 711/808 934

SOURCE: echomail via fidonet.ozzmosis.com

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.