* bsd_helper.cc (ipcexit_hookthread): Fix whitespace and handle leak.
* bsd_mutex.cc: Include stdlib.h, sys/msg.h and sys/sem.h. (mtx_init): Initialize lock counter to 0. (_mtx_lock): Increment and log mutex lock counter. (mtx_owned): Add winpid argument. Return true only if mutex is actually owned by process winpid. (_mtx_assert): Add winpid argument accordingly. (_mtx_unlock): Log owner and lock count. (MSLEEP_MUTEX): Remove. (MSLEEP_SEM): Ditto. (MSLEEP_EVENT): Ditto. (msleep_event_name): Ditto. (msleep_cs): New global critical section. (msleep_cnt): New global variable indicating msleep record usage. (msleep_max_cnt): New global variable indicating msleep record size. (msleep_arr): New global pointer to msleep records. (msleep_init): Initialize msleep_cs. Allocate msleep_arr array. (_msleep): Rewrite using new msleep_cs/msleep_arr based thread synchronization. Don't be shy with debug output. (wakeup): Rewrite using new msleep_cs/msleep_arr based thread synchronization. * bsd_mutex.h (struct mtx): Add lock counter for better debugging. (mtx_owned): Declare with winpid argument. (_mtx_assert): Ditto. (mtx_assert): Define with winpid argument. * cygserver.cc (version): Remove. (SERVER_VERSION): New define, decoupling server version information from source code control system. (print_version): Simplify printing server version. * process.cc (process::process): Fix wrong bracketing (and handle leak). (process::~process): Only try to close _signal_arrived if valid. * sysv_sem.cc: Include sys/smallprint.h. (semundo_clear): Define with additional struct thread pointer argument. Accomodate throughout. (SEMUNDO_LOCKASSERT): Define with winpid argument. Accomodate throughout. (struct sem_undo): Define un_proc as pid_t on Cygwin. Accomodate throughout. (seminit): Improve debugging by adding the semid to the mutex name. (semget): Correctly print key value as 64 bit hex value in debug output. (semexit_myhook): Remove Cygwin specific unlocking of mutexes owned by exiting process. Keep semaphore global lock throughout whole function to avoid races. * sysv_shm.cc (GIANT_REQUIRED): Define empty on Cygwin. We know that Giant is locked.
This commit is contained in:
		| @@ -14,6 +14,9 @@ details. */ | ||||
| #define __BSD_VISIBLE 1 | ||||
| #include <sys/smallprint.h> | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/msg.h> | ||||
| #include <sys/sem.h> | ||||
|  | ||||
| #include "process.h" | ||||
| #include "cygserver_ipc.h" | ||||
| @@ -26,6 +29,7 @@ mtx_init (mtx *m, const char *name, const void *, int) | ||||
| { | ||||
|   m->name = name; | ||||
|   m->owner = 0; | ||||
|   m->cnt = 0; | ||||
|   /* Can't use Windows Mutexes here since Windows Mutexes are only | ||||
|      unlockable by the lock owner. */ | ||||
|   m->h = CreateSemaphore (NULL, 1, 1, NULL); | ||||
| @@ -36,30 +40,32 @@ mtx_init (mtx *m, const char *name, const void *, int) | ||||
| void | ||||
| _mtx_lock (mtx *m, DWORD winpid, const char *file, int line) | ||||
| { | ||||
|   _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name); | ||||
|   _log (file, line, LOG_DEBUG, "Try locking mutex %s (%u) (hold: %u)", | ||||
| 	m->name, winpid, m->owner); | ||||
|   if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0) | ||||
|     _panic (file, line, "wait for %s in %d failed, %E", m->name, winpid); | ||||
|   m->owner = winpid; | ||||
|   _log (file, line, LOG_DEBUG, "Locked      mutex %s", m->name); | ||||
|   _log (file, line, LOG_DEBUG, "Locked      mutex %s/%u (%u)", | ||||
| 	m->name, ++m->cnt, winpid); | ||||
| } | ||||
|  | ||||
| int | ||||
| mtx_owned (mtx *m) | ||||
| mtx_owned (mtx *m, DWORD winpid) | ||||
| { | ||||
|   return m->owner > 0; | ||||
|   return m->owner == winpid; | ||||
| } | ||||
|  | ||||
| void | ||||
| _mtx_assert (mtx *m, int what, const char *file, int line) | ||||
| _mtx_assert (mtx *m, int what, DWORD winpid, const char *file, int line) | ||||
| { | ||||
|   switch (what) | ||||
|     { | ||||
|       case MA_OWNED: | ||||
|         if (!mtx_owned (m)) | ||||
|         if (!mtx_owned (m, winpid)) | ||||
| 	  _panic (file, line, "Mutex %s not owned", m->name); | ||||
| 	break; | ||||
|       case MA_NOTOWNED: | ||||
|         if (mtx_owned (m)) | ||||
|         if (mtx_owned (m, winpid)) | ||||
| 	  _panic (file, line, "Mutex %s is owned", m->name); | ||||
|         break; | ||||
|       default: | ||||
| @@ -70,6 +76,8 @@ _mtx_assert (mtx *m, int what, const char *file, int line) | ||||
| void | ||||
| _mtx_unlock (mtx *m, const char *file, int line) | ||||
| { | ||||
|   DWORD owner = m->owner; | ||||
|   unsigned long cnt = m->cnt; | ||||
|   m->owner = 0; | ||||
|   /* Cautiously check if mtx_destroy has been called (shutdown). | ||||
|      In that case, m->h is NULL. */ | ||||
| @@ -82,7 +90,8 @@ _mtx_unlock (mtx *m, const char *file, int line) | ||||
|           || (wincap.is_winnt () && GetLastError () != ERROR_TOO_MANY_POSTS)) | ||||
| 	_panic (file, line, "release of mutex %s failed, %E", m->name); | ||||
|     } | ||||
|   _log (file, line, LOG_DEBUG, "Unlocked    mutex %s", m->name); | ||||
|   _log (file, line, LOG_DEBUG, "Unlocked    mutex %s/%u (owner: %u)", | ||||
|   	m->name, cnt, owner); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -98,22 +107,6 @@ mtx_destroy (mtx *m) | ||||
|  * Helper functions for msleep/wakeup. | ||||
|  */ | ||||
|  | ||||
| /* Values for which */ | ||||
| #define MSLEEP_MUTEX	0 | ||||
| #define MSLEEP_SEM	1 | ||||
| #define MSLEEP_EVENT	2 | ||||
|  | ||||
| static char * | ||||
| msleep_event_name (void *ident, char *name, int which) | ||||
| { | ||||
|   if (wincap.has_terminal_services ()) | ||||
|     __small_sprintf (name, "Global\\cygserver.msleep.evt.%1d.%08x", | ||||
| 		     which, ident); | ||||
|   else | ||||
|     __small_sprintf (name, "cygserver.msleep.evt.%1d.%08x", which, ident); | ||||
|   return name; | ||||
| } | ||||
|  | ||||
| static int | ||||
| win_priority (int priority) | ||||
| { | ||||
| @@ -172,13 +165,36 @@ set_priority (int priority) | ||||
|  * flag the mutex is not entered before returning. | ||||
|  */ | ||||
| static HANDLE msleep_glob_evt; | ||||
| CRITICAL_SECTION msleep_cs; | ||||
| static long msleep_cnt; | ||||
| static long msleep_max_cnt; | ||||
| static struct msleep_record { | ||||
|   void *ident; | ||||
|   HANDLE wakeup_evt; | ||||
|   LONG threads; | ||||
| } *msleep_arr; | ||||
|  | ||||
| void | ||||
| msleep_init (void) | ||||
| { | ||||
|   extern struct msginfo msginfo; | ||||
|   extern struct seminfo seminfo; | ||||
|  | ||||
|   msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); | ||||
|   if (!msleep_glob_evt) | ||||
|     panic ("CreateEvent in msleep_init failed: %E"); | ||||
|   InitializeCriticalSection (&msleep_cs); | ||||
|   long msgmni = support_msgqueues ? msginfo.msgmni : 0; | ||||
|   long semmni = support_semaphores ? seminfo.semmni : 0; | ||||
|   TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni); | ||||
|   TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni); | ||||
|   debug ("Try allocating msgmni (%d) + semmni (%d) msleep records", | ||||
|   	 msgmni, semmni); | ||||
|   msleep_max_cnt = msgmni + semmni; | ||||
|   msleep_arr = (struct msleep_record *) calloc (msleep_max_cnt, | ||||
|   						sizeof (struct msleep_record)); | ||||
|   if (!msleep_arr) | ||||
|     panic ("Allocating msleep records in msleep_init failed: %d", errno); | ||||
| } | ||||
|  | ||||
| int | ||||
| @@ -186,46 +202,47 @@ _msleep (void *ident, struct mtx *mtx, int priority, | ||||
| 	const char *wmesg, int timo, struct thread *td) | ||||
| { | ||||
|   int ret = -1; | ||||
|   char name[64]; | ||||
|   int i; | ||||
|  | ||||
|   /* The mutex is used to indicate an ident specific critical section. | ||||
|      The critical section is needed to synchronize access to the | ||||
|      semaphore and eventually the event object.  The whole idea is | ||||
|      that a wakeup is *guaranteed* to wakeup *all* threads.  If that's | ||||
|      not synchronized, sleeping threads could return into the msleep | ||||
|      function before all other threads have called CloseHandle(evt). | ||||
|      That's bad, since the event still exists and is signalled! */ | ||||
|   HANDLE mutex = CreateMutex (NULL, FALSE, | ||||
| 			      msleep_event_name (ident, name, MSLEEP_MUTEX)); | ||||
|   if (!mutex) | ||||
|     panic ("CreateMutex in msleep (%s) failed: %E", wmesg); | ||||
|   WaitForSingleObject (mutex, INFINITE); | ||||
|   while (1) | ||||
|     { | ||||
|       EnterCriticalSection (&msleep_cs); | ||||
|       for (i = 0; i < msleep_cnt; ++i) | ||||
| 	if (msleep_arr[i].ident == ident) | ||||
| 	  break; | ||||
|       if (!msleep_arr[i].ident) | ||||
| 	{ | ||||
| 	  debug ("New ident %x, index %d", ident, i); | ||||
| 	  if (i >= msleep_max_cnt) | ||||
| 	    panic ("Too many idents to wait for.\n"); | ||||
| 	  msleep_arr[i].ident = ident; | ||||
| 	  msleep_arr[i].wakeup_evt = CreateEvent (NULL, TRUE, FALSE, NULL); | ||||
| 	  if (!msleep_arr[i].wakeup_evt) | ||||
| 	    panic ("CreateEvent in msleep (%s) failed: %E", wmesg); | ||||
| 	  msleep_arr[i].threads = 1; | ||||
| 	  ++msleep_cnt; | ||||
| 	  LeaveCriticalSection (&msleep_cs); | ||||
| 	  break; | ||||
| 	} | ||||
|       else if (WaitForSingleObject (msleep_arr[i].wakeup_evt, 0) | ||||
| 	       != WAIT_OBJECT_0) | ||||
| 	{ | ||||
| 	  ++msleep_arr[i].threads; | ||||
| 	  LeaveCriticalSection (&msleep_cs); | ||||
| 	  break; | ||||
| 	} | ||||
|       /* Otherwise wakeup has been called, so sleep to wait until all | ||||
|          formerly waiting threads have left and retry. */ | ||||
|       LeaveCriticalSection (&msleep_cs); | ||||
|       Sleep (1L); | ||||
|     } | ||||
|  | ||||
|   /* Ok, we're in the critical section now.  We create an ident specific | ||||
|      semaphore, which is used to synchronize the waiting threads. */ | ||||
|   HANDLE sem = CreateSemaphore (NULL, 0, LONG_MAX, | ||||
| 				msleep_event_name (ident, name, MSLEEP_SEM)); | ||||
|   if (!sem) | ||||
|     panic ("CreateSemaphore in msleep (%s) failed: %E", wmesg); | ||||
|  | ||||
|   /* This thread is one more thread sleeping.  The semaphore value is | ||||
|      so used as a counter of sleeping threads.  That info is needed by | ||||
|      the wakeup function. */ | ||||
|   ReleaseSemaphore (sem, 1, NULL); | ||||
|  | ||||
|   /* Leave critical section. */ | ||||
|   ReleaseMutex (mutex); | ||||
|  | ||||
|   HANDLE evt = CreateEvent (NULL, TRUE, FALSE, | ||||
| 			    msleep_event_name (ident, name, MSLEEP_EVENT)); | ||||
|   if (!evt) | ||||
|     panic ("CreateEvent in msleep (%s) failed: %E", wmesg); | ||||
|   if (mtx) | ||||
|     mtx_unlock (mtx); | ||||
|   int old_priority = set_priority (priority); | ||||
|   HANDLE obj[4] = | ||||
|     { | ||||
|       evt, | ||||
|       msleep_arr[i].wakeup_evt, | ||||
|       msleep_glob_evt, | ||||
|       td->client->handle (), | ||||
|       td->client->signal_arrived () | ||||
| @@ -241,37 +258,45 @@ _msleep (void *ident, struct mtx *mtx, int priority, | ||||
|     { | ||||
|       case WAIT_OBJECT_0:	/* wakeup() has been called. */ | ||||
| 	ret = 0; | ||||
| 	debug ("msleep wakeup called"); | ||||
|         break; | ||||
|       case WAIT_OBJECT_0 + 1:	/* Shutdown event (triggered by wakeup_all). */ | ||||
|         priority |= PDROP; | ||||
| 	/*FALLTHRU*/ | ||||
|       case WAIT_OBJECT_0 + 2:	/* The dependent process has exited. */ | ||||
| 	debug ("msleep process exit or shutdown"); | ||||
| 	ret = EIDRM; | ||||
|         break; | ||||
|       case WAIT_OBJECT_0 + 3:	/* Signal for calling process arrived. */ | ||||
| 	debug ("msleep process got signal"); | ||||
|         ret = EINTR; | ||||
| 	break; | ||||
|       case WAIT_TIMEOUT: | ||||
|         ret = EWOULDBLOCK; | ||||
|         break; | ||||
|       default: | ||||
| 	panic ("wait in msleep (%s) failed, %E", wmesg); | ||||
| 	/* There's a chance that a process has been terminated before | ||||
| 	   WaitForMultipleObjects has been called.  In this case the handles | ||||
| 	   might be invalid.  The error code returned is ERROR_INVALID_HANDLE. | ||||
| 	   Since we can trust the values of these handles otherwise, we | ||||
| 	   treat an ERROR_INVALID_HANDLE as a normal process termination and | ||||
| 	   hope for the best. */ | ||||
| 	if (GetLastError () != ERROR_INVALID_HANDLE) | ||||
| 	  panic ("wait in msleep (%s) failed, %E", wmesg); | ||||
| 	ret = EIDRM; | ||||
| 	break; | ||||
|     } | ||||
|  | ||||
|   CloseHandle (evt); | ||||
|   /* wakeup has reset the semaphore to 0.  Now indicate that this thread | ||||
|      has called CloseHandle (evt) and enter the critical section.  The | ||||
|      critical section is still hold by wakeup, until all formerly sleeping | ||||
|      threads have indicated that the event has been dismissed.  That's | ||||
|      the signal for wakeup that it's the only thread still holding a | ||||
|      handle to the event object.  wakeup will then close the last handle | ||||
|      and leave the critical section. */ | ||||
|   ReleaseSemaphore (sem, 1, NULL); | ||||
|   WaitForSingleObject (mutex, INFINITE); | ||||
|   CloseHandle (sem); | ||||
|   ReleaseMutex (mutex); | ||||
|   CloseHandle (mutex); | ||||
|   EnterCriticalSection (&msleep_cs); | ||||
|   if (--msleep_arr[i].threads == 0) | ||||
|     { | ||||
|       CloseHandle (msleep_arr[i].wakeup_evt); | ||||
|       msleep_arr[i].ident = NULL; | ||||
|       --msleep_cnt; | ||||
|       if (i < msleep_cnt) | ||||
|         msleep_arr[i] = msleep_arr[msleep_cnt]; | ||||
|     } | ||||
|   LeaveCriticalSection (&msleep_cs); | ||||
|  | ||||
|   set_priority (old_priority); | ||||
|  | ||||
| @@ -286,70 +311,15 @@ _msleep (void *ident, struct mtx *mtx, int priority, | ||||
| int | ||||
| wakeup (void *ident) | ||||
| { | ||||
|   char name[64]; | ||||
|   LONG threads; | ||||
|  | ||||
|   HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, | ||||
|   			  msleep_event_name (ident, name, MSLEEP_EVENT)); | ||||
|   if (!evt) /* No thread is waiting. */ | ||||
|     { | ||||
|       /* Another round of different error codes returned by 9x and NT | ||||
|          systems. Oh boy... */ | ||||
|       if (  (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME) | ||||
| 	  || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND)) | ||||
| 	panic ("OpenEvent (%s) in wakeup failed: %E", name); | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|   /* The mutex is used to indicate an ident specific critical section. | ||||
|      The critical section is needed to synchronize access to the | ||||
|      semaphore and eventually the event object.  The whole idea is | ||||
|      that a wakeup is *guaranteed* to wakeup *all* threads.  If that's | ||||
|      not synchronized, sleeping threads could return into the msleep | ||||
|      function before all other threads have called CloseHandle(evt). | ||||
|      That's bad, since the event still exists and is signalled! */ | ||||
|   HANDLE mutex = OpenMutex (MUTEX_ALL_ACCESS, FALSE, | ||||
| 			    msleep_event_name (ident, name, MSLEEP_MUTEX)); | ||||
|   if (!mutex) | ||||
|     panic ("OpenMutex (%s) in wakeup failed: %E", name); | ||||
|   WaitForSingleObject (mutex, INFINITE); | ||||
|   /* Ok, we're in the critical section now.  We create an ident specific | ||||
|      semaphore, which is used to synchronize the waiting threads. */ | ||||
|   HANDLE sem = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE, | ||||
| 			      msleep_event_name (ident, name, MSLEEP_SEM)); | ||||
|   if (!sem) | ||||
|     panic ("OpenSemaphore (%s) in wakeup failed: %E", name); | ||||
|   ReleaseSemaphore (sem, 1, &threads); | ||||
|   /* `threads' is the number of waiting threads.  Now reset the semaphore | ||||
|      to 0 and wait for this number of threads to indicate that they have | ||||
|      called CloseHandle (evt).  Then it's save to do the same here in | ||||
|      wakeup, which then means that the event object is destroyed and | ||||
|      can get safely recycled. */ | ||||
|   for (int i = threads + 1; i > 0; --i) | ||||
|     WaitForSingleObject (sem, INFINITE); | ||||
|  | ||||
|   if (!SetEvent (evt)) | ||||
|     panic ("SetEvent (%s) in wakeup failed, %E", name); | ||||
|  | ||||
|   /* Now wait for all threads which were waiting for this wakeup. */ | ||||
|   while (threads-- > 0) | ||||
|     WaitForSingleObject (sem, INFINITE); | ||||
|  | ||||
|   /* Now our handle is the last handle to this event object. */ | ||||
|   CloseHandle (evt); | ||||
|   /* But paranoia rulez, so we check here again. */ | ||||
|   evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, | ||||
| 		   msleep_event_name (ident, name, MSLEEP_EVENT)); | ||||
|   if (evt) | ||||
|     panic ("Event %s has not been destroyed.  Obviously I can't count :-(", | ||||
| 	   name); | ||||
|  | ||||
|   CloseHandle (sem); | ||||
|  | ||||
|   /* Leave critical section (all of wakeup is critical). */ | ||||
|   ReleaseMutex (mutex); | ||||
|   CloseHandle (mutex); | ||||
|   int i; | ||||
|  | ||||
|   EnterCriticalSection (&msleep_cs); | ||||
|   for (i = 0; i < msleep_cnt; ++i) | ||||
|     if (msleep_arr[i].ident == ident) | ||||
|       break; | ||||
|   if (msleep_arr[i].ident) | ||||
|     SetEvent (msleep_arr[i].wakeup_evt); | ||||
|   LeaveCriticalSection (&msleep_cs); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user