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 >
Wrap
C/C++ Source or Header
|
2002-08-08
|
7KB
|
306 lines
/*
*@@sourcefile sem.c:
* implements fast mutex semaphores a la Win32
* critical sections.
*
* This is an OS/2 implementation of what Win32 calls as
* "critical sections"
* It is an implementation that is highly optimized for
* the case where there is only one thread trying to
* access the critical section, i.e. it is available most
* of the time. Therefore we can use these critical
* sections for all our serialization and not lose any
* performance when concurrent access is unlikely.
*
* In case there is multiple access, we use the OS/2 kernel
* event semaphores.
*
* Function prefix:
*
* -- sem*: semaphore helpers.
*
*@@added V0.9.20 (2002-08-04) [umoeller]
*@@header "helpers\semaphores.h"
*/
/*
* Copyright (C) 2002 Sander van Leeuwen.
* Copyright (C) 2002 Ulrich Möller.
* This file is part of the "XWorkplace helpers" source package.
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS
#define INCL_WINMESSAGEMGR
#include <os2.h>
#include <stdlib.h>
#include "setup.h" // code generation and debugging options
#include "helpers\sem.h"
#pragma hdrstop
/*
*@@category: Helpers\Control program helpers\Semaphores
* see sem.c.
*/
// encode PID and TID into one 32bit value
#define MAKE_THREADID(processid, threadid) ((processid << 16) | threadid)
/*
*@@ GetTID:
*
*/
ULONG GetTID(VOID)
{
PTIB ptib;
PPIB ppib;
APIRET rc;
if (!(rc = DosGetInfoBlocks(&ptib, &ppib)))
return MAKE_THREADID(ppib->pib_ulpid,
ptib->tib_ptib2->tib2_ultid);
return 0;
}
/*
*@@ GetPID:
*
*/
ULONG GetPID(VOID)
{
PTIB ptib;
PPIB ppib;
APIRET rc;
if (!(rc = DosGetInfoBlocks(&ptib, &ppib)))
return ppib->pib_ulpid;
return 0;
}
/*
*@@ semCreate:
* the equivalent to DosCreateMutexSem.
*/
APIRET semCreate(PFASTMTX pmtx,
PCSZ pszSemName)
{
APIRET rc;
if (!pmtx)
return ERROR_INVALID_PARAMETER;
// initialize lock count with special value -1, meaning noone posesses it
pmtx->LockCount = -1;
pmtx->RecursionCount = 0;
pmtx->OwningThread = 0;
if (rc = DosCreateEventSem((PSZ)pszSemName,
&pmtx->hmtxLock,
(pszSemName) ? DC_SEM_SHARED : 0,
0))
pmtx->hmtxLock = 0;
pmtx->Reserved = GetPID();
return rc;
}
/*
*@@ semOpen:
* the equivalent to DosOpenMutexSem.
*/
APIRET semOpen(PFASTMTX pmtx,
PCSZ pszSemName)
{
HMTX hmtxLock = 0;
APIRET rc;
if (!pszSemName)
return ERROR_INVALID_PARAMETER;
return DosOpenEventSem((PSZ)pszSemName, &hmtxLock);
}
/*
*@@ semClose:
* the equivalent to DosCloseMutexSem.
*/
APIRET semClose(PFASTMTX pmtx)
{
APIRET rc;
if (!pmtx)
return ERROR_INVALID_PARAMETER;
if (!pmtx->hmtxLock)
return ERROR_INVALID_HANDLE;
if (pmtx->RecursionCount) /* Should not happen */
return ERROR_SEM_BUSY;
pmtx->LockCount = -1;
pmtx->RecursionCount = 0;
pmtx->OwningThread = 0;
rc = DosCloseEventSem(pmtx->hmtxLock);
pmtx->hmtxLock = 0;
pmtx->Reserved = (ULONG)-1;
return rc;
}
/*
*@@ semRequest:
* the equivalent to DosRequestMutexSem.
*/
APIRET semRequest(PFASTMTX pmtx)
{
ULONG threadid = GetTID();
if (!pmtx)
return ERROR_INVALID_PARAMETER;
// create pmtx sect just in time...
if (!pmtx->hmtxLock)
semCreate(pmtx, NULL);
// do an atomic increase of the lockcounter and see if it is > 0
// (i.e. it is already posessed)
if (DosInterlockedIncrement(&pmtx->LockCount))
{
// semaphore was already requested:
testenter:
// if the same thread is requesting it again, memorize it
if (pmtx->OwningThread == threadid)
{
pmtx->RecursionCount++;
return NO_ERROR;
}
// current owner is different thread thread:
// do an atomic operation where we compare the owning thread id with 0
// and if this is true, exchange it with the id of the current thread
if (DosInterlockedCompareExchange((PLONG)&pmtx->OwningThread, threadid, 0))
{
// the compare did not return equal, i.e. the pmtx sect is in use
ULONG cPosts;
APIRET rc;
/* Now wait for it */
if (rc = DosWaitEventSem(pmtx->hmtxLock, SEM_INDEFINITE_WAIT))
return rc;
DosResetEventSem(pmtx->hmtxLock, &cPosts);
// multiple waiters could be running now. Repeat the logic so that
// only one actually can get the critical section
goto testenter;
}
}
pmtx->OwningThread = GetTID();
pmtx->RecursionCount = 1;
return NO_ERROR;
}
/*
*@@ semAssert:
* returns TRUE if the current thread currently owns
* the mutex.
*
*/
BOOL semAssert(PFASTMTX pmtx)
{
return ( (pmtx)
&& (pmtx->OwningThread)
&& (pmtx->OwningThread == GetTID())
);
}
/*
*@@ semTry:
*
*/
BOOL semTry(PFASTMTX pmtx)
{
if (DosInterlockedIncrement(&pmtx->LockCount))
{
if (pmtx->OwningThread == GetTID())
{
pmtx->RecursionCount++;
return TRUE;
}
DosInterlockedDecrement(&pmtx->LockCount);
return FALSE;
}
pmtx->OwningThread = GetTID();
pmtx->RecursionCount = 1;
return TRUE;
}
/*
*@@ semRelease:
* the equivalent of DosReleaseMutexSem.
*/
APIRET semRelease(PFASTMTX pmtx)
{
if (!pmtx)
return ERROR_INVALID_PARAMETER;
if (pmtx->OwningThread != GetTID())
return ERROR_NOT_OWNER;
if (--pmtx->RecursionCount)
{
DosInterlockedDecrement(&pmtx->LockCount );
return NO_ERROR;
}
pmtx->OwningThread = 0;
if (DosInterlockedDecrement(&pmtx->LockCount) >= 0)
// someone is waiting
DosPostEventSem(pmtx->hmtxLock);
return NO_ERROR;
}