This is the mail archive of the
pthreads-win32@sources.redhat.com
mailing list for the pthreas-win32 project.
Re: WINCE: problems w/CRITICAL_SECTION
- From: "Craig A. Vanderborgh" <craigv at voxware dot com>
- To: Ross Johnson <rpj at ise dot canberra dot edu dot au>
- Cc: pthreads-win32 at sources dot redhat dot com
- Date: 03 Mar 2003 10:12:05 -0700
- Subject: Re: WINCE: problems w/CRITICAL_SECTION
- References: <3E5FE3E8.1040009@ise.canberra.edu.au>
On Fri, 2003-02-28 at 15:34, Ross Johnson wrote:
> It's difficult to imagine how critical sections could be behaving
> differently under WinCE and that anything would be working if they did.
> They either block or they don't. They're so simple they don't even
> return a result value. Does GetLastError indicate anything?
>
> Would it be possible for you to provide a code snippet where you use
> CV's and where you use CS's?
>
Why sure. This is going to be kind of long and I apologize for that,
but I think you'll agree it's necessary. The main construct we have
been using is something we call "critsect", which has the semantics of a
mutex/cv pair. This CRITSECT creation of ours is the only
synchronization object used in our application. In the first (working)
version, pthread_mutex_t is defined to be HANDLE. The main players are
CRITSECT_ENTER et al. given here:
/* critsect.h - critical section abstraction for pthreads & WIN32
*---------------------------------------------------------------------------*
* DESCRIPTION
* CRITSECT should be used any time a mutex/cv pair is needed for
* inter-thread synchronization; this will maintain portability between
* WIN32 and real programming environments.
*---------------------------------------------------------------------------*/
#ifndef _CRITSECT_H_
#define _CRITSECT_H_
#include "pthr.h"
#include <errno.h>
#include "types.h"
#include "ifaces.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CRITSECT_NOERROR 0
#define CRITSECT_TIMEDOUT 1
#define CRITSECT_INVALID 2
#define CRITSECT_NOMEMORY 3
#ifndef VBX_SINGLE_THREAD
#if defined(REAL_PTHREADS) || defined(VXWORKS)
/**** Multithreaded Pthreads Version ****/
#define CRITSECT_ENTER(cs) pthread_mutex_lock((cs)->mutexPtr)
#define CRITSECT_LEAVE(cs) pthread_mutex_unlock((cs)->mutexPtr)
#define CRITSECT_SIGNAL(cs) pthread_cond_signal(&cs->cv)
#define CRITSECT_TIMEDWAIT_ENTER(cs, wait, timedout, predicate) \
{ \
struct timespec abstime; \
Int retval; \
pthread_get_expiration_np(wait, &abstime); \
pthread_mutex_lock(cs->mutexPtr); \
while (predicate && !timedout) \
timedout = pthread_cond_timedwait(&cs->cv, cs->mutexPtr, &abstime); \
if (timedout) { \
if (timedout == ETIMEDOUT) timedout = CRITSECT_TIMEDOUT; \
else if (timedout == ENOMEM) timedout = CRITSECT_NOMEMORY; \
else timedout = CRITSECT_INVALID; \
} \
}
#define CRITSECT_WAIT_ENTER(cs, predicate) { \
pthread_mutex_lock(cs->mutexPtr); \
while (predicate) \
pthread_cond_wait(&cs->cv, cs->mutexPtr); \
}
typedef struct CritSect_s {
pthread_mutex_t * mutexPtr;
pthread_cond_t cv;
IMemory * memory;
} CritSect_t, *CRITSECT;
#endif
#if defined(WIN32) && !defined(REAL_PTHREADS)
/**** Multithreaded WIN32 Version ****/
#include <assert.h>
#include "vbx.h"
#define CRITSECT_ENTER(cs) pthread_mutex_lock((cs)->mutexPtr)
#define CRITSECT_LEAVE(cs) pthread_mutex_unlock((cs)->mutexPtr)
#define CRITSECT_SIGNAL(cs) SetEvent((cs)->event)
#define CRITSECT_WAIT_ENTER(cs, predicate) { \
int retval; \
while (predicate) { \
(void) WaitForSingleObject(cs->event, INFINITE); \
if (predicate) { \
ResetEvent(cs->event); \
Sleep(1); \
} \
} \
retval = WaitForSingleObject(*cs->mutexPtr, INFINITE); \
assert(retval == WAIT_OBJECT_0 || retval == WAIT_ABANDONED); \
ResetEvent(cs->event); \
}
#define CRITSECT_TIMEDWAIT_ENTER(cs, wait, timedout, predicate) { \
HANDLE handle[2] = {cs->event, *cs->mutexPtr}; \
Int retval; \
Int msec = (wait)->tv_nsec / 1000000; \
msec += (wait)->tv_sec * 1000; \
if (msec <= 0) msec = 1; \
timedout = CRITSECT_NOERROR; \
while (predicate && !timedout) { \
retval = WaitForSingleObject(cs->event, msec); \
if (retval != 0 && retval != WAIT_OBJECT_0) { \
if (retval == WAIT_TIMEOUT) \
timedout = CRITSECT_TIMEDOUT; \
else { \
timedout = CRITSECT_INVALID; \
VBX_print(" CRITSECT_TIMEDWAIT_ENTER: retval %d last error = %d\n", retval, GetLastError());\
} \
} else { \
timedout = CRITSECT_NOERROR; \
if (predicate && !timedout) { \
ResetEvent(cs->event); \
Sleep(1); \
} \
} \
} \
if (!timedout && retval == WAIT_OBJECT_0) { \
retval = WaitForSingleObject(*cs->mutexPtr, INFINITE); \
} \
ResetEvent(cs->event); \
}
typedef struct CritSect_s {
pthread_mutex_t * mutexPtr;
HANDLE event;
IMemory * memory;
} CritSect_t, *CRITSECT;
#endif
CRITSECT CRITSECT_create(IMemory * memory, CRITSECT parent);
void CRITSECT_destroy(CRITSECT cs);
#define CRITSECT_DECLARE(C) CRITSECT C;
#define CRITSECT_CREATE(C,M,P) ((C) = CRITSECT_create(M, P))
#define CRITSECT_DESTROY(C) if (C) CRITSECT_destroy(C)
#define CRITSECT_EXISTS(C) (C)
#define CRITSECT_ASSIGN(C,CS) (C) = (CS)
#define TIMESPEC struct timespec
#define CRITSECT_SET_TIMESPEC(T,USEC) (T).tv_sec = (USEC)/1000000, (T).tv_nsec = ((USEC)%1000000)*1000
#define MUTEX_DECLARE(M) pthread_mutex_t M;
#define MUTEX_INIT(MP, ATTR) !pthread_mutex_init(MP, ATTR)
#define MUTEX_DESTROY(MP) ((MP) && !pthread_mutex_destroy(MP))
#define MUTEX_LOCK(MP) pthread_mutex_lock(MP);
#define MUTEX_UNLOCK(MP) pthread_mutex_unlock(MP);
#else /* defined VBX_SINGLE_THREAD */
/**** Single-Threaded Version ****/
#define CRITSECT_DECLARE(C)
#define CRITSECT_CREATE(C,M,P) TRUE
#define CRITSECT_DESTROY(C)
#define CRITSECT_EXISTS(C) TRUE
#define CRITSECT_ASSIGN(C,CS)
#define CRITSECT_ENTER(C)
#define CRITSECT_WAIT_ENTER(C,P)
#define CRITSECT_TIMEDWAIT_ENTER(C,W,T,P) (T) = ((P) ? CRITSECT_TIMEDOUT : CRITSECT_NOERROR)
#define CRITSECT_LEAVE(C)
#define CRITSECT_SIGNAL(C)
#define TIMESPEC Int
#define CRITSECT_SET_TIMESPEC(T,USEC) (T) = (USEC)
#define MUTEX_DECLARE(MP)
#define MUTEX_INIT(MP, ATTR) TRUE
#define MUTEX_DESTROY(MP) TRUE
#define MUTEX_LOCK(MP)
#define MUTEX_UNLOCK(MP)
#endif /* not defined VBX_SINGLE_THREAD */
#ifdef __cplusplus
}
#endif
#endif /* _CRITSECT_H_ */
This particular version contains the WIN32 flavor that uses CreateMutexW/WaitForSingleObject.
This is the one that works. The other version, the one that DOES NOT work uses the Micro$oft
critical section routines and we define pthread_mutex_t to be CRITICAL_SECTION. Its WIN32
section is the only different part and appears as follows:
.
.
.
#if defined(WIN32) && !defined(REAL_PTHREADS)
/**** Multithreaded WIN32 Version ****/
#include <assert.h>
#include "vbx.h"
#define CRITSECT_ENTER(cs) pthread_mutex_lock((cs)->mutexPtr)
#define CRITSECT_LEAVE(cs) pthread_mutex_unlock((cs)->mutexPtr)
#define CRITSECT_SIGNAL(cs) SetEvent((cs)->event)
#define CRITSECT_WAIT_ENTER(cs, predicate) { \
int retval; \
while (predicate) { \
(void) WaitForSingleObject(cs->event, INFINITE); \
if (predicate) { \
ResetEvent(cs->event); \
Sleep(1); \
} \
} \
EnterCriticalSection(cs->mutexPtr); \
ResetEvent(cs->event); \
}
#define CRITSECT_TIMEDWAIT_ENTER(cs, wait, timedout, predicate) { \
Int retval; \
Int msec = (wait)->tv_nsec / 1000000; \
msec += (wait)->tv_sec * 1000; \
if (msec <= 0) msec = 1; \
timedout = CRITSECT_NOERROR; \
while (predicate && !timedout) { \
retval = WaitForSingleObject(cs->event, msec); \
if (retval != 0 && retval != WAIT_OBJECT_0) { \
if (retval == WAIT_TIMEOUT) \
timedout = CRITSECT_TIMEDOUT; \
else { \
timedout = CRITSECT_INVALID; \
VBX_print(" CRITSECT_TIMEDWAIT_ENTER: retval %d last error = %d\n", retval, GetLastError());\
} \
} else { \
timedout = CRITSECT_NOERROR; \
if (predicate && !timedout) { \
ResetEvent(cs->event); \
Sleep(1); \
} \
} \
} \
if (!timedout && retval == WAIT_OBJECT_0) { \
EnterCriticalSection(cs->mutexPtr); \
} \
ResetEvent(cs->event); \
}
typedef struct CritSect_s {
pthread_mutex_t * mutexPtr;
HANDLE event;
IMemory * memory;
} CritSect_t, *CRITSECT;
#endif
An oft-used example of CRITSECT is our mailbox facility. Here are our mailbox
retrieval/insertion routines:
/* Mailbox type */
typedef struct MBOX_s {
CRITSECT_DECLARE(notEmpty)
IMemory * memory;
PMBLVL levels;
Uns numMessages;
Uns growQuantum;
Uns maxMessages;
Uns nPriorities;
} MBOX_t;
/* Macro to extract a message from a mailbox */
#define MBOX_GETMSG(mailbox, message) { \
Uns priority; \
PMBLVL level; \
message = NULL; \
for (priority = 0; priority < mailbox->nPriorities; priority++) { \
level = mailbox->levels + priority; \
if (level->numMessages) { \
message = level->storage[level->firstMessage++]; \
if (level->firstMessage == level->currentSize) level->firstMessage = 0; \
level->numMessages--; \
mailbox->numMessages--; \
CRITSECT_SIGNAL(level->notFull); \
break; \
} \
} \
}
/* Macro to insert a message into a mailbox at a given priority level */
#define MBOX_PUTMSG(mailbox, level, message) { \
if (level->numMessages >= level->currentSize) { \
OBJT * storage = level->storage; \
Uns oldSize = level->currentSize; \
assert(level->lastMessage == level->firstMessage); \
if ((level->currentSize += mailbox->growQuantum) > mailbox->maxMessages) \
level->currentSize = mailbox->maxMessages; \
level->storage = (OBJT *) mailbox->memory->Calloc(mailbox->memory, level->currentSize, sizeof(OBJT
), FAST_MEMORY); \
VBX_DEBUG(VBX_print(" Enlarging MBOX level @%p: old (%d @%p), new (%d @%p)\n", level, oldSize, st
orage, level->currentSize, level->storage)); \
memcpy(level->storage, storage, level->lastMessage * sizeof(OBJT)); \
memcpy(level->storage + (level->currentSize - oldSize + level->firstMessage), \
storage + level->firstMessage, (oldSize - level->firstMessage) * sizeof(OBJT)); \
mailbox->memory->Free(mailbox->memory, storage); \
level->firstMessage = level->currentSize - oldSize + level->firstMessage; \
} \
level->storage[level->lastMessage++] = message; \
if (level->lastMessage == level->currentSize) level->lastMessage = 0; \
level->numMessages++; \
mailbox->numMessages++; \
CRITSECT_SIGNAL(mailbox->notEmpty); \
}
Bool
MBOX_insert(MBOX mailbox, OBJT message)
/*---------------------------------------------------------------------------*
* DESCRIPTION
* Insert a message with the lowest priority into a prioritized mailbox,
* waiting until space at that priority is available
*
* Usage: success = MBOX_insert(mailbox, message);
*
* Bool success; TRUE
* MBOX mailbox; mailbox to insert the message into
* OBJT message; message to insert into the mailbox
*
* Return: TRUE if successful
*---------------------------------------------------------------------------*/
{
PMBLVL level = mailbox->levels;
CRITSECT_WAIT_ENTER(level->notFull, level->numMessages >= mailbox->maxMessages);
if (level->numMessages < mailbox->maxMessages) {
MBOX_PUTMSG(mailbox, level, message);
CRITSECT_LEAVE(level->notFull);
return(TRUE);
} else {
CRITSECT_LEAVE(level->notFull);
return(FALSE);
}
}
OBJT
MBOX_remove(MBOX mailbox)
/*---------------------------------------------------------------------------*
* DESCRIPTION
* Remove a message from a prioritized mailbox, waiting if none is available
* Usage: message = MBOX_remove(mailbox);
*
* OBJT message; message removed from the mailbox
* MBOX mailbox; mailbox to remove the message from
*
* Return: message OBJT if message is removed
*---------------------------------------------------------------------------*/
{
OBJT message = (OBJT) NULL;
CRITSECT_WAIT_ENTER(mailbox->notEmpty, mailbox->numMessages == 0);
if (mailbox->numMessages > 0)
MBOX_GETMSG(mailbox, message);
CRITSECT_LEAVE(mailbox->notEmpty);
return message;
}
Hopefully this will give you some idea of what we're doing. I'm sorry that it
is a fair amount of stuff to have to look at - the good news is that we do
exclusively use JUST THIS ONE (critsect) mechanism in our application. Also,
you should know that both "craig-threads" and pthreads-win32, with both using
Micro$oft CRITCAL_SECTION as the mutex, work okay in our simpler tests. Just
not in our application. Thus I am a little stuck right now because I am unsure
about how to debug this. Any ideas you might have would be greatly appreciated!
regards,
craig vanderborgh
voxware incorporated
> Ross
>
> PS. I'm not sure your message went to anyone but me. I was the only
> addressee, contrary to your 'Hello' line.
>
> Craig A. Vanderborgh wrote:
>
> >Hello Ross, John, and pthreads-win32:
> >
> >Background:
> >We have a very big and extremely nasty pthreads application that is
> >mature and has been thoroughly tested. Basically, it is a continuous
> >speech recognizer that operates in real time. We have had it running
> on
> >wince for a long while under what I call "craig-threads" - it's a
> >pthreads subset that lacks condition variable support. I became
> >interested in using pthreads-win32 to get complete support for
> condition
> >variables and a few other things.
> >
> >Our application does not work correctly on pthreads-win32 though, and
> >for an interesting reason. It appears that the M$ CriticalSection
> stuff
> >isn't working correctly on WINCE, at least for our application. I did
> >the following experiments:
> >
> >1. Ran application under pthreads-win32. Result: unexpected hang "way
> >down there"
> >2. Ran application under craig-threads w/CreateMutexW and associated
> >stuff for pthread_mutex_*. Result: works
> >3. Ran application under crag-threads w/InitializeCriticalSection and
> >associated stuff for pthread_mutex_*. Result: same exact behavior as
> 1.
> >above.
> >
> >So it would seem that the M$ CRITICAL_SECTION stuff is implicated. I
> >have read that we will be paying a performance penalty if we have to
> use
> >a M$ mutex instead of CRITICAL_SECTION. Is this true, and how bad is
> >it? Our application uses synchronization objects with a very high
> >bandwidth, btw. Do you think I should modify pthreads-win32 so that
> one
> >could optionally use mutex/WaitForSingleObject in place of
> >CRITICAL_SECTION? Is there some way I might be able to debug the
> >CRITICAL_SECTION version(s) of our application? Please advise.
> >
> >regards,
> >craig vanderborgh
> >voxware incorporated
> >
> >
> >
> >
>