This is the mail archive of the pthreads-win32@sources.redhat.com mailing list for the pthreas-win32 project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: WINCE: problems w/CRITICAL_SECTION


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
> >
> >
> >  
> >
> 



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]