2001-11-12 Dick Porter <dick@ximian.com>
authorDick Porter <dick@acm.org>
Mon, 12 Nov 2001 08:59:21 +0000 (08:59 -0000)
committerDick Porter <dick@acm.org>
Mon, 12 Nov 2001 08:59:21 +0000 (08:59 -0000)
* semaphores.c: Implement semaphores

* wait.c (wait_for_item): Maintain a wait count rather than count
signalled booleans.

* threads.c (thread_wait_multiple): Don't lock the wait item, that
will block other wait threads

2001-11-11  Dick Porter  <dick@ximian.com>

* Makefile.am: Rename some automake variables
(from Nick Drochak <ndrochak@gol.com>)

svn path=/trunk/mono/; revision=1329

mono/io-layer/ChangeLog
mono/io-layer/Makefile.am
mono/io-layer/semaphores.c [new file with mode: 0644]
mono/io-layer/semaphores.h [new file with mode: 0644]
mono/io-layer/threads.c
mono/io-layer/wait-private.h
mono/io-layer/wait.c
mono/io-layer/wapi-private.h
mono/io-layer/wapi.h

index 2e003048d845d55508e0c675bcad24a7b7b280b4..922517b641f1e41223604f589f559e1f11266cd3 100644 (file)
@@ -1,3 +1,18 @@
+2001-11-12  Dick Porter  <dick@ximian.com>
+
+       * semaphores.c: Implement semaphores
+
+       * wait.c (wait_for_item): Maintain a wait count rather than count
+       signalled booleans.
+
+       * threads.c (thread_wait_multiple): Don't lock the wait item, that
+       will block other wait threads
+
+2001-11-11  Dick Porter  <dick@ximian.com>
+
+       * Makefile.am: Rename some automake variables
+       (from Nick Drochak <ndrochak@gol.com>)
+
 2001-11-10  Dick Porter  <dick@ximian.com>
 
        * Makefile.am (libwapiincludedir): Fix include destination
index 0e9d088659bfa36f498e20c24be8689ab61b8790..ea30fe05710ada37ae7d6acc24e918bfad14eea7 100644 (file)
@@ -13,6 +13,7 @@ OTHER_H = \
        handles.h       \
        io.h            \
        io-layer.h      \
+       semaphores.h    \
        status.h        \
        threads.h       \
        uglify.h        \
@@ -20,7 +21,7 @@ OTHER_H = \
        wapi.h          \
        #
 
-OTHER_SOURCES = \
+OTHER_SRC = \
        error.c                 \
        error.h                 \
        handles.c               \
@@ -31,6 +32,8 @@ OTHER_SOURCES = \
        io-layer.h              \
        pthread-compat.c        \
        pthread-compat.h        \
+       semaphores.c            \
+       semaphores.h            \
        status.h                \
        threads.c               \
        threads.h               \
@@ -50,22 +53,22 @@ WINDOWS_H = \
        io-layer.h              \
        #
 
-WINDOWS_SOURCES = \
+WINDOWS_SRC = \
        io-layer.h              \
        io-layer-dummy.c        \
        #
 
 if PLATFORM_WIN32
-libwapi_a_SOURCES = $(WINDOWS_SOURCES)
+libwapi_a_SOURCES = $(WINDOWS_SRC)
 libwapiinclude_HEADERS = $(WINDOWS_H)
 else
-libwapi_a_SOURCES = $(OTHER_SOURCES)
+libwapi_a_SOURCES = $(OTHER_SRC)
 libwapiinclude_HEADERS = $(OTHER_H)
 endif
 
 EXTRA_DIST =   \
-       $(WINDOWS_SOURCES)      \
-       $(OTHER_SOURCES)        \
+       $(WINDOWS_SRC)  \
+       $(OTHER_SRC)    \
        #
 
 
diff --git a/mono/io-layer/semaphores.c b/mono/io-layer/semaphores.c
new file mode 100644 (file)
index 0000000..71a2e24
--- /dev/null
@@ -0,0 +1,331 @@
+#include <config.h>
+#include <glib.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "mono/io-layer/wapi.h"
+#include "wapi-private.h"
+#include "wait-private.h"
+
+#define DEBUG
+
+/* emulate sem_t, so that we can prod the internal state more easily */
+struct _WapiHandle_sem
+{
+       WapiHandle handle;
+       guint32 val;
+       gint32 max;
+};
+
+/* This mutex controls access to _all_ semaphores and should not be
+ * locked for long periods.
+ *
+ * This global mutex and cond is really for wait_multiple, so we dont
+ * have to try and lock multiple handle mutexes and conditions.
+ */
+static pthread_mutex_t sem_mutex=PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t sem_cond=PTHREAD_COND_INITIALIZER;
+
+static void sema_close(WapiHandle *handle);
+static gboolean sema_wait(WapiHandle *handle, guint32 ms);
+static guint32 sema_wait_multiple(gpointer data);
+
+static struct _WapiHandleOps sem_ops = {
+       sema_close,             /* close */
+       NULL,                   /* getfiletype */
+       NULL,                   /* readfile */
+       NULL,                   /* writefile */
+       NULL,                   /* seek */
+       NULL,                   /* setendoffile */
+       NULL,                   /* getfilesize */
+       sema_wait,              /* wait */
+       sema_wait_multiple,     /* wait_multiple */
+};
+
+static void sema_close(WapiHandle *handle)
+{
+       /* Not really much to do here */
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": closing sem handle %p", handle);
+#endif
+}
+
+static gboolean sema_wait(WapiHandle *handle G_GNUC_UNUSED, guint32 ms G_GNUC_UNUSED)
+{
+       struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle;
+       gboolean waited;
+       int ret;
+       
+       pthread_mutex_lock(&sem_mutex);
+       
+       /* Check state first */
+       if(sem_handle->val>0) {
+               waited=TRUE;
+               goto end;
+       }
+       
+       if(ms==INFINITE) {
+               ret=pthread_cond_wait(&sem_cond, &sem_mutex);
+               waited=TRUE;
+       } else {
+               struct timespec timeout;
+               struct timeval now;
+               div_t divvy;
+               
+               divvy=div((int)ms, 1000);
+               gettimeofday(&now, NULL);
+               
+               timeout.tv_sec=now.tv_sec+divvy.quot;
+               timeout.tv_nsec=(now.tv_usec+divvy.rem)*1000;
+               
+               ret=pthread_cond_timedwait(&sem_cond, &sem_mutex, &timeout);
+               if(ret==0) {
+                       waited=TRUE;
+               } else {
+                       /* ret might be ETIMEDOUT for timeout, or
+                        * other for error */
+                       waited=FALSE;
+               }
+       }
+       
+end:
+       if(waited==TRUE) {
+               sem_handle->val--;
+       }
+       
+       pthread_mutex_unlock(&sem_mutex);
+       return(waited);
+}
+
+static guint32 sema_wait_multiple(gpointer data G_GNUC_UNUSED)
+{
+       WaitQueueItem *item=(WaitQueueItem *)data;
+       guint32 numhandles, count;
+       struct timespec timeout;
+       struct timeval now;
+       div_t divvy;
+       guint32 i;
+       int ret;
+       
+       numhandles=item->handles[WAPI_HANDLE_SEM]->len;
+       
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": waiting on %d sem handles for %d ms", numhandles,
+                 item->timeout);
+#endif
+
+       pthread_mutex_lock(&sem_mutex);
+       
+       /* First, check if any of the handles are already signalled.
+        * If waitall is specified we only return if all handles have
+        * been signalled.
+        */
+       for(count=0, i=0; i<numhandles; i++) {
+               struct _WapiHandle_sem *sem_handle;
+               
+               sem_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_SEM],
+                                            i);
+               if(sem_handle->val>0) {
+                       count++;
+               }
+       }
+       
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": Preliminary check found %d handles signalled", count);
+#endif
+
+       if((item->waitall==TRUE && count==numhandles) ||
+          (item->waitall==FALSE && count>0)) {
+               goto success;
+       }
+       
+       /* OK, we need to wait for some */
+       if(item->timeout!=INFINITE) {
+               divvy=div((int)item->timeout, 1000);
+               gettimeofday(&now, NULL);
+               
+               timeout.tv_sec=now.tv_sec+divvy.quot;
+               timeout.tv_nsec=(now.tv_usec+divvy.rem)*1000;
+       }
+       
+       /* We can restart from here without resetting the timeout,
+        * because it is calculated from absolute time, not an offset.
+        */
+again:
+       if(item->timeout==INFINITE) {
+               ret=pthread_cond_wait(&sem_cond, &sem_mutex);
+       } else {
+               ret=pthread_cond_timedwait(&sem_cond, &sem_mutex, &timeout);
+       }
+       
+       if(ret==ETIMEDOUT) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
+#endif
+
+               goto success;
+       }
+
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": Sem posted, checking status");
+#endif
+
+       /* A semaphore was posted, so see if it was one we are
+        * interested in
+        */
+       for(count=0, i=0; i<numhandles; i++) {
+               struct _WapiHandle_sem *sem_handle;
+               
+               sem_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_SEM],
+                                            i);
+               if(sem_handle->val>0) {
+                       count++;
+               }
+       }
+
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION
+                 ": Check after sem post found %d handles signalled", count);
+#endif
+
+       if((item->waitall==TRUE && count==numhandles) ||
+          (item->waitall==FALSE && count>0)) {
+               goto success;
+       }
+       
+       /* Either we have waitall set with more handles to wait for, or
+        * the sem that was posted wasn't interesting to us
+        */
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": Waiting a bit longer");
+#endif
+
+       goto again;
+       
+success:
+       item->waited[WAPI_HANDLE_SEM]=TRUE;
+       item->waitcount[WAPI_HANDLE_SEM]=count;
+
+       if((item->waitall==TRUE && count==numhandles) ||
+          (item->waitall==FALSE && count>0)) {
+               /* Decrease all waited semaphores */
+               for(i=0; i<numhandles; i++) {
+                       struct _WapiHandle_sem *sem_handle;
+                       
+                       sem_handle=g_ptr_array_index(
+                               item->handles[WAPI_HANDLE_SEM], i);
+                       if(sem_handle->val>0) {
+                               sem_handle->val--;
+                       }
+               }
+       }
+
+       pthread_mutex_unlock(&sem_mutex);
+
+       return(count);
+}
+
+/**
+ * CreateSemaphore:
+ * @security: Ignored for now.
+ * @initial: The initial count for the semaphore.  The value must be
+ * greater than or equal to zero, and less than or equal to @max.
+ * @max: The maximum count for this semaphore.  The value must be
+ * greater than zero.
+ * @name: Pointer to a string specifying the name of this semaphore,
+ * or %NULL.  Currently ignored.
+ *
+ * Creates a new semaphore handle.  A semaphore is signalled when its
+ * count is greater than zero, and unsignalled otherwise.  The count
+ * is decreased by one whenever a wait function releases a thread that
+ * was waiting for the semaphore.  The count is increased by calling
+ * ReleaseSemaphore().
+ *
+ * Return value: a new handle, or NULL
+ */
+WapiHandle *CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const guchar *name G_GNUC_UNUSED)
+{
+       struct _WapiHandle_sem *sem_handle;
+       WapiHandle *handle;
+       
+       if(max<=0) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": max <= 0");
+#endif
+
+               return(NULL);
+       }
+       
+       if(initial>max || initial<0) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": initial>max or < 0");
+#endif
+
+               return(NULL);
+       }
+       
+       sem_handle=(struct _WapiHandle_sem *)g_new0(struct _WapiHandle_sem, 1);
+       handle=(WapiHandle *)sem_handle;
+       _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_SEM, sem_ops);
+
+       sem_handle->val=initial;
+       sem_handle->max=max;
+
+#ifdef DEBUG
+       g_message(G_GNUC_PRETTY_FUNCTION ": Created semaphore handle %p",
+                 handle);
+#endif
+       
+       return(handle);
+}
+
+/**
+ * ReleaseSemaphore:
+ * @handle: The semaphore handle to release.
+ * @count: The amount by which the semaphore's count should be
+ * increased.
+ * @prevcount: Pointer to a location to store the previous count of
+ * the semaphore, or %NULL.
+ *
+ * Increases the count of semaphore @handle by @count.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ */
+gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount)
+{
+       struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle;
+       gboolean ret=FALSE;
+       
+       
+       pthread_mutex_lock(&sem_mutex);
+       
+       /* Do this before checking for count overflow, because overflowing max
+        * is a listed technique for finding the current value
+        */
+       if(prevcount!=NULL) {
+               *prevcount=sem_handle->val;
+       }
+       
+       /* No idea why max is signed, but thats the spec :-( */
+       if(sem_handle->val+count > (guint32)sem_handle->max) {
+#ifdef DEBUG
+               g_message(G_GNUC_PRETTY_FUNCTION ": sem %p max value would be exceeded: max %d current %d count %d",
+                         handle, sem_handle->max, sem_handle->val, count);
+#endif
+
+               goto end;
+       }
+       
+       sem_handle->val+=count;
+       pthread_cond_broadcast(&sem_cond);
+       ret=TRUE;
+       
+end:
+       pthread_mutex_unlock(&sem_mutex);
+       return(ret);
+}
diff --git a/mono/io-layer/semaphores.h b/mono/io-layer/semaphores.h
new file mode 100644 (file)
index 0000000..21a8bd0
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _WAPI_SEMAPHORES_H_
+#define _WAPI_SEMAPHORES_H_
+
+#include <glib.h>
+
+extern WapiHandle *CreateSemaphore(WapiSecurityAttributes *security, gint32 initial, gint32 max, const guchar *name);
+extern gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount);
+
+#endif /* _WAPI_SEMAPHORES_H_ */
index e3e6c67e594cf2f3a23da691b2ac2eb106d679dc..f8ca4d8538dfdf9d248106fe588173306477d4ff 100644 (file)
@@ -117,8 +117,6 @@ static guint32 thread_wait_multiple(gpointer data)
        struct timeval now;
        div_t divvy;
        
-       pthread_mutex_lock(&item->mutex);
-       
        numhandles=item->handles[WAPI_HANDLE_THREAD]->len;
        
 #ifdef DEBUG
@@ -213,7 +211,8 @@ again:
 
 success:
        item->waited[WAPI_HANDLE_THREAD]=TRUE;
-       pthread_mutex_unlock(&item->mutex);
+       item->waitcount[WAPI_HANDLE_THREAD]=count;
+       
        return(count);
 }
 
index 3be5e19b11a61278193dcb7aa5fe26349094c1c2..cde9758f2f82c3f39df6d0ed8f569feaef2d8d77 100644 (file)
@@ -27,6 +27,7 @@ typedef struct _WaitQueueItem
        GPtrArray *handles[WAPI_HANDLE_COUNT];
        TimedThread *thread[WAPI_HANDLE_COUNT];
        gboolean waited[WAPI_HANDLE_COUNT];
+       guint32 waitcount[WAPI_HANDLE_COUNT];
 } WaitQueueItem;
 
 #endif /* _WAPI_WAIT_PRIVATE_H_ */
index ab642aa2a962546cd3ab5a017f8be08ed7e92678..921b41195cf880c32411f0fbf7990cff8906a1dc 100644 (file)
@@ -263,7 +263,7 @@ static gboolean wait_for_item(WaitQueueItem *item)
        if(item->waitall==TRUE) {
                ret=TRUE;
                for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-                       if(_wapi_handle_count_signalled(item->handles[i])!=item->handles[i]->len) {
+                       if(item->waitcount[i]!=item->handles[i]->len) {
                                ret=FALSE;
                                break;
                        }
@@ -271,7 +271,7 @@ static gboolean wait_for_item(WaitQueueItem *item)
        } else {
                ret=FALSE;
                for(i=0; i<WAPI_HANDLE_COUNT; i++) {
-                       if(_wapi_handle_count_signalled(item->handles[i])>0) {
+                       if(item->waitcount[i]>0) {
                                ret=TRUE;
                                break;
                        }
index f434555132e25ceb277cde15ca4df22b60322858..e40585f819c257c5a66219a4dd1f46608ad102ec 100644 (file)
@@ -10,6 +10,7 @@ typedef enum {
        WAPI_HANDLE_FILE,
        WAPI_HANDLE_CONSOLE,
        WAPI_HANDLE_THREAD,
+       WAPI_HANDLE_SEM,
        WAPI_HANDLE_COUNT,
 } WapiHandleType;
 
index c77af554e8788ff3e4dfb30c60e54f053f924202..84f52cc0230ceecc4e8ca2f1892d76599c26d2b7 100644 (file)
@@ -4,8 +4,9 @@
 #include <mono/io-layer/error.h>
 #include <mono/io-layer/handles.h>
 #include <mono/io-layer/io.h>
-#include <mono/io-layer/threads.h>
+#include <mono/io-layer/semaphores.h>
 #include <mono/io-layer/status.h>
+#include <mono/io-layer/threads.h>
 #include <mono/io-layer/wait.h>
 
 #endif /* _WAPI_WAPI_H_ */