home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / sem.c < prev    next >
C/C++ Source or Header  |  2002-08-08  |  7KB  |  306 lines

  1.  
  2. /*
  3.  *@@sourcefile sem.c:
  4.  *      implements fast mutex semaphores a la Win32
  5.  *      critical sections.
  6.  *
  7.  *      This is an OS/2 implementation of what Win32 calls as
  8.  *      "critical sections"
  9.  *      It is an implementation that is highly optimized for
  10.  *      the case where there is only one thread trying to
  11.  *      access the critical section, i.e. it is available most
  12.  *      of the time. Therefore we can use these critical
  13.  *      sections for all our serialization and not lose any
  14.  *      performance when concurrent access is unlikely.
  15.  *
  16.  *      In case there is multiple access, we use the OS/2 kernel
  17.  *      event semaphores.
  18.  *
  19.  *      Function prefix:
  20.  *
  21.  *      --  sem*: semaphore helpers.
  22.  *
  23.  *@@added V0.9.20 (2002-08-04) [umoeller]
  24.  *@@header "helpers\semaphores.h"
  25.  */
  26.  
  27. /*
  28.  *      Copyright (C) 2002 Sander van Leeuwen.
  29.  *      Copyright (C) 2002 Ulrich Möller.
  30.  *      This file is part of the "XWorkplace helpers" source package.
  31.  *      This is free software; you can redistribute it and/or modify
  32.  *      it under the terms of the GNU General Public License as published
  33.  *      by the Free Software Foundation, in version 2 as it comes in the
  34.  *      "COPYING" file of the XWorkplace main distribution.
  35.  *      This program is distributed in the hope that it will be useful,
  36.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  37.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  38.  *      GNU General Public License for more details.
  39.  */
  40.  
  41. #define OS2EMX_PLAIN_CHAR
  42.     // this is needed for "os2emx.h"; if this is defined,
  43.     // emx will define PSZ as _signed_ char, otherwise
  44.     // as unsigned char
  45.  
  46. #define INCL_DOSPROCESS
  47. #define INCL_DOSSEMAPHORES
  48. #define INCL_DOSERRORS
  49.  
  50. #define INCL_WINMESSAGEMGR
  51. #include <os2.h>
  52.  
  53. #include <stdlib.h>
  54.  
  55. #include "setup.h"                      // code generation and debugging options
  56.  
  57. #include "helpers\sem.h"
  58.  
  59. #pragma hdrstop
  60.  
  61. /*
  62.  *@@category: Helpers\Control program helpers\Semaphores
  63.  *      see sem.c.
  64.  */
  65.  
  66. // encode PID and TID into one 32bit value
  67. #define MAKE_THREADID(processid, threadid)  ((processid << 16) | threadid)
  68.  
  69. /*
  70.  *@@ GetTID:
  71.  *
  72.  */
  73.  
  74. ULONG GetTID(VOID)
  75. {
  76.     PTIB   ptib;
  77.     PPIB   ppib;
  78.     APIRET rc;
  79.  
  80.     if (!(rc = DosGetInfoBlocks(&ptib, &ppib)))
  81.         return MAKE_THREADID(ppib->pib_ulpid,
  82.                              ptib->tib_ptib2->tib2_ultid);
  83.  
  84.     return 0;
  85. }
  86.  
  87. /*
  88.  *@@ GetPID:
  89.  *
  90.  */
  91.  
  92. ULONG GetPID(VOID)
  93. {
  94.     PTIB   ptib;
  95.     PPIB   ppib;
  96.     APIRET rc;
  97.  
  98.     if (!(rc = DosGetInfoBlocks(&ptib, &ppib)))
  99.         return ppib->pib_ulpid;
  100.  
  101.     return 0;
  102. }
  103.  
  104. /*
  105.  *@@ semCreate:
  106.  *      the equivalent to DosCreateMutexSem.
  107.  */
  108.  
  109. APIRET semCreate(PFASTMTX pmtx,
  110.                  PCSZ pszSemName)
  111. {
  112.     APIRET rc;
  113.  
  114.     if (!pmtx)
  115.         return ERROR_INVALID_PARAMETER;
  116.  
  117.     // initialize lock count with special value -1, meaning noone posesses it
  118.     pmtx->LockCount      = -1;
  119.     pmtx->RecursionCount = 0;
  120.     pmtx->OwningThread   = 0;
  121.  
  122.     if (rc = DosCreateEventSem((PSZ)pszSemName,
  123.                                &pmtx->hmtxLock,
  124.                                (pszSemName) ? DC_SEM_SHARED : 0,
  125.                                0))
  126.         pmtx->hmtxLock = 0;
  127.  
  128.     pmtx->Reserved = GetPID();
  129.  
  130.     return rc;
  131. }
  132.  
  133. /*
  134.  *@@ semOpen:
  135.  *      the equivalent to DosOpenMutexSem.
  136.  */
  137.  
  138. APIRET semOpen(PFASTMTX pmtx,
  139.                PCSZ pszSemName)
  140. {
  141.     HMTX   hmtxLock = 0;
  142.     APIRET rc;
  143.  
  144.     if (!pszSemName)
  145.         return ERROR_INVALID_PARAMETER;
  146.  
  147.     return DosOpenEventSem((PSZ)pszSemName, &hmtxLock);
  148. }
  149.  
  150. /*
  151.  *@@ semClose:
  152.  *      the equivalent to DosCloseMutexSem.
  153.  */
  154.  
  155. APIRET semClose(PFASTMTX pmtx)
  156. {
  157.     APIRET rc;
  158.  
  159.     if (!pmtx)
  160.         return ERROR_INVALID_PARAMETER;
  161.  
  162.     if (!pmtx->hmtxLock)
  163.         return ERROR_INVALID_HANDLE;
  164.  
  165.     if (pmtx->RecursionCount)  /* Should not happen */
  166.         return ERROR_SEM_BUSY;
  167.  
  168.     pmtx->LockCount      = -1;
  169.     pmtx->RecursionCount = 0;
  170.     pmtx->OwningThread   = 0;
  171.     rc = DosCloseEventSem(pmtx->hmtxLock);
  172.     pmtx->hmtxLock       = 0;
  173.     pmtx->Reserved       = (ULONG)-1;
  174.  
  175.     return rc;
  176. }
  177.  
  178. /*
  179.  *@@ semRequest:
  180.  *      the equivalent to DosRequestMutexSem.
  181.  */
  182.  
  183. APIRET semRequest(PFASTMTX pmtx)
  184. {
  185.     ULONG   threadid = GetTID();
  186.  
  187.     if (!pmtx)
  188.         return ERROR_INVALID_PARAMETER;
  189.  
  190.     // create pmtx sect just in time...
  191.     if (!pmtx->hmtxLock)
  192.         semCreate(pmtx, NULL);
  193.  
  194.     // do an atomic increase of the lockcounter and see if it is > 0
  195.     // (i.e. it is already posessed)
  196.     if (DosInterlockedIncrement(&pmtx->LockCount))
  197.     {
  198.         // semaphore was already requested:
  199.  
  200. testenter:
  201.         // if the same thread is requesting it again, memorize it
  202.         if (pmtx->OwningThread == threadid)
  203.         {
  204.             pmtx->RecursionCount++;
  205.             return NO_ERROR;
  206.         }
  207.  
  208.         // current owner is different thread thread:
  209.  
  210.         // do an atomic operation where we compare the owning thread id with 0
  211.         // and if this is true, exchange it with the id of the current thread
  212.         if (DosInterlockedCompareExchange((PLONG)&pmtx->OwningThread, threadid, 0))
  213.         {
  214.             // the compare did not return equal, i.e. the pmtx sect is in use
  215.  
  216.             ULONG   cPosts;
  217.             APIRET  rc;
  218.  
  219.             /* Now wait for it */
  220.             if (rc = DosWaitEventSem(pmtx->hmtxLock, SEM_INDEFINITE_WAIT))
  221.                 return rc;
  222.  
  223.             DosResetEventSem(pmtx->hmtxLock, &cPosts);
  224.  
  225.             // multiple waiters could be running now. Repeat the logic so that
  226.             // only one actually can get the critical section
  227.             goto testenter;
  228.         }
  229.     }
  230.  
  231.     pmtx->OwningThread   = GetTID();
  232.     pmtx->RecursionCount = 1;
  233.  
  234.     return NO_ERROR;
  235. }
  236.  
  237. /*
  238.  *@@ semAssert:
  239.  *      returns TRUE if the current thread currently owns
  240.  *      the mutex.
  241.  *
  242.  */
  243.  
  244. BOOL semAssert(PFASTMTX pmtx)
  245. {
  246.     return (    (pmtx)
  247.              && (pmtx->OwningThread)
  248.              && (pmtx->OwningThread == GetTID())
  249.            );
  250. }
  251.  
  252. /*
  253.  *@@ semTry:
  254.  *
  255.  */
  256.  
  257. BOOL semTry(PFASTMTX pmtx)
  258. {
  259.     if (DosInterlockedIncrement(&pmtx->LockCount))
  260.     {
  261.         if (pmtx->OwningThread == GetTID())
  262.         {
  263.             pmtx->RecursionCount++;
  264.             return TRUE;
  265.         }
  266.  
  267.         DosInterlockedDecrement(&pmtx->LockCount);
  268.  
  269.         return FALSE;
  270.     }
  271.  
  272.     pmtx->OwningThread   = GetTID();
  273.     pmtx->RecursionCount = 1;
  274.  
  275.     return TRUE;
  276. }
  277.  
  278. /*
  279.  *@@ semRelease:
  280.  *      the equivalent of DosReleaseMutexSem.
  281.  */
  282.  
  283. APIRET semRelease(PFASTMTX pmtx)
  284. {
  285.     if (!pmtx)
  286.         return ERROR_INVALID_PARAMETER;
  287.  
  288.     if (pmtx->OwningThread != GetTID())
  289.         return ERROR_NOT_OWNER;
  290.  
  291.     if (--pmtx->RecursionCount)
  292.     {
  293.         DosInterlockedDecrement(&pmtx->LockCount );
  294.         return NO_ERROR;
  295.     }
  296.  
  297.     pmtx->OwningThread = 0;
  298.  
  299.     if (DosInterlockedDecrement(&pmtx->LockCount) >= 0)
  300.         // someone is waiting
  301.         DosPostEventSem(pmtx->hmtxLock);
  302.  
  303.     return NO_ERROR;
  304. }
  305.  
  306.