From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 19933 invoked by alias); 3 Mar 2003 17:07:47 -0000 Mailing-List: contact pthreads-win32-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: pthreads-win32-owner@sources.redhat.com Received: (qmail 19893 invoked from network); 3 Mar 2003 17:07:46 -0000 Received: from unknown (HELO calypsoo.voxware.com) (63.167.41.125) by 172.16.49.205 with SMTP; 3 Mar 2003 17:07:46 -0000 Received: from zetar (12-253-95-100.client.attbi.com [12.253.95.100]) by calypsoo.voxware.com with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2653.13) id F5WAH9ZY; Mon, 3 Mar 2003 12:07:44 -0500 Subject: Re: WINCE: problems w/CRITICAL_SECTION From: "Craig A. Vanderborgh" To: Ross Johnson Cc: pthreads-win32@sources.redhat.com In-Reply-To: <3E5FE3E8.1040009@ise.canberra.edu.au> References: <3E5FE3E8.1040009@ise.canberra.edu.au> Content-Type: text/plain Content-Transfer-Encoding: 7bit Date: Mon, 03 Mar 2003 17:07:00 -0000 Message-Id: <1046711527.11071.157.camel@zetar> Mime-Version: 1.0 X-SW-Source: 2003/txt/msg00035.txt.bz2 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 #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 #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 #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 > > > > > > > > >