[PATCH 3/9] server: Use native (posix) semaphores

Daniel Santos daniel.santos at pobox.com
Sun Sep 13 17:16:00 CDT 2015


Changes server to use a posix semaphore instead of an int. This patch
alone would be pointless, but creates a mechanism for client processes
to interact with the same semaphores.
---
 server/Makefile.in |   2 +-
 server/object.h    |   4 +
 server/semaphore.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++------
 server/thread.c    |  54 ++++++++++-
 4 files changed, 297 insertions(+), 35 deletions(-)

diff --git a/server/Makefile.in b/server/Makefile.in
index 19a4fac..48a4522 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -1,4 +1,4 @@
-EXTRALIBS = $(POLL_LIBS) $(RT_LIBS)
+EXTRALIBS = $(POLL_LIBS) $(RT_LIBS) $(PTHREAD_LIBS)
 
 C_SRCS = \
 	async.c \
diff --git a/server/object.h b/server/object.h
index b59811f..ead4c50 100644
--- a/server/object.h
+++ b/server/object.h
@@ -90,6 +90,10 @@ struct object_ops
     int (*close_handle)(struct object *,struct process *,obj_handle_t);
     /* destroy on refcount == 0 */
     void (*destroy)(struct object *);
+    /* like signaled(), but tries to get the lock, returns 1 upon success */
+    int (*trylock)(struct object *,struct wait_queue_entry *);
+    /* undo a previously sucessful call to trylock */
+    void (*trylock_undo)(struct object *,struct wait_queue_entry *);
 };
 
 struct object
diff --git a/server/semaphore.c b/server/semaphore.c
index d87325c..6900804 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -26,6 +26,21 @@
 #include <stdlib.h>
 #include <stdarg.h>
 
+#ifdef ENABLE_HYBRID_SYNC
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <errno.h>
+# ifdef HAVE_SEMAPHORE_H
+#  include <semaphore.h>
+# endif
+# ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+# endif
+# ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
+#endif
+
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
 #include "windef.h"
@@ -36,10 +51,17 @@
 #include "request.h"
 #include "security.h"
 
+#define NATIVE_SEMAPHORE_MAX_NAME (32)
 struct semaphore
 {
     struct object  obj;    /* object header */
+#ifdef ENABLE_HYBRID_SYNC
+    sem_t         *p;
+    unsigned int   key;
+    char           name[NATIVE_SEMAPHORE_MAX_NAME];
+#else
     unsigned int   count;  /* current count */
+#endif /* ENABLE_HYBRID_SYNC */
     unsigned int   max;    /* maximum possible count */
 };
 
@@ -49,6 +71,14 @@ static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entr
 static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static unsigned int semaphore_map_access( struct object *obj, unsigned int access );
 static int semaphore_signal( struct object *obj, unsigned int access );
+static void semaphore_new( struct semaphore *sem, unsigned int initial );
+static unsigned int semaphore_get_value( struct semaphore *sem );
+
+#ifdef ENABLE_HYBRID_SYNC
+static void semaphore_destroy( struct object *obj );
+static int semaphore_trylock( struct object *obj, struct wait_queue_entry *entry );
+static void semaphore_trylock_undo( struct object *obj, struct wait_queue_entry *entry );
+#endif /* ENABLE_HYBRID_SYNC */
 
 static const struct object_ops semaphore_ops =
 {
@@ -67,35 +97,32 @@ static const struct object_ops semaphore_ops =
     no_lookup_name,                /* lookup_name */
     no_open_file,                  /* open_file */
     no_close_handle,               /* close_handle */
-    no_destroy                     /* destroy */
+#ifdef ENABLE_HYBRID_SYNC
+    semaphore_destroy,             /* destroy */
+    semaphore_trylock,             /* trylock */
+    semaphore_trylock_undo         /* trylock_undo */
+#else
+    no_destroy,                    /* destroy */
+    NULL,                          /* trylock */
+    NULL                           /* trylock_undo */
+#endif
 };
 
+#ifndef ENABLE_HYBRID_SYNC
 
-static struct semaphore *create_semaphore( struct directory *root, const struct unicode_str *name,
-                                           unsigned int attr, unsigned int initial, unsigned int max,
-                                           const struct security_descriptor *sd )
+static inline void semaphore_new( struct semaphore *sem, unsigned int initial )
 {
-    struct semaphore *sem;
+    sem->count = initial;
+}
 
-    if (!max || (initial > max))
-    {
-        set_error( STATUS_INVALID_PARAMETER );
-        return NULL;
-    }
-    if ((sem = create_named_object_dir( root, name, attr, &semaphore_ops )))
-    {
-        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
-        {
-            /* initialize it if it didn't already exist */
-            sem->count = initial;
-            sem->max   = max;
-            if (sd) default_set_sd( &sem->obj, sd, OWNER_SECURITY_INFORMATION|
-                                                   GROUP_SECURITY_INFORMATION|
-                                                   DACL_SECURITY_INFORMATION|
-                                                   SACL_SECURITY_INFORMATION );
-        }
-    }
-    return sem;
+static inline unsigned int semaphore_get_value( struct semaphore *sem )
+{
+    return sem->count;
+}
+
+static inline void semaphore_down( struct semaphore *sem )
+{
+    --sem->count;
 }
 
 static int release_semaphore( struct semaphore *sem, unsigned int count,
@@ -119,12 +146,188 @@ static int release_semaphore( struct semaphore *sem, unsigned int count,
     }
     return 1;
 }
+#else  /* ENABLE_HYBRID_SYNC */
+
+static DWORD next_semaphore_id = 0;
+
+static void semaphore_new( struct semaphore *sem, unsigned int initial )
+{
+    /* imperfect, but good enough for now */
+    int tries;
+    for (tries = 0; tries < 0x10000; ++tries)
+    {
+        DWORD pid = (DWORD)getpid();
+        DWORD reverse_pid = 0;
+        DWORD bit;
+
+        for (bit = (1 << (sizeof(DWORD) * 8 - 1)); bit && pid; bit >>= 1, pid >>= 1)
+        {
+            if (pid & 1)
+                reverse_pid |= bit;
+        }
+
+        sem->key = reverse_pid ^ next_semaphore_id++;
+        snprintf(sem->name, sizeof(sem->name) - 1, "/wine-sem-%08x", sem->key);
+        sem->p = sem_open(sem->name, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, initial);
+
+        if (sem->p != SEM_FAILED)
+        {
+            /* fprintf(stderr, "semaphore_new: initial = %u, sem = %p, sem->p = %p, sem->name = %s\n",
+                    initial, sem, sem->p, sem->name); */
+            return;
+        }
+
+        switch (errno)
+        {
+        case EACCES:
+        case EEXIST:
+            continue;
+
+        case EINVAL:
+            set_error( STATUS_INVALID_PARAMETER );
+            break;
+        case EMFILE:
+        case ENFILE:
+            set_error( STATUS_TOO_MANY_OPENED_FILES );
+            break;
+        case ENOMEM:
+            set_error( STATUS_NO_MEMORY );
+            break;
+        default:
+            break;
+        }
+        perror("sem_open");
+        assert(0);
+        exit(1);
+    }
+    fprintf(stderr, "failed to create unique key for semaphore\n");
+    assert(0);
+}
+
+static unsigned int semaphore_get_value( struct semaphore *sem )
+{
+    int ret;
+
+    if (sem_getvalue(sem->p, &ret) == -1)
+    {
+        perror("sem_getvalue");
+        exit(1);
+
+        return STATUS_INVALID_HANDLE;
+    }
+
+    return (unsigned int)ret;
+}
+
+static void semaphore_destroy( struct object *obj )
+{
+    struct semaphore *sem = (struct semaphore *)obj;
+    assert( obj->ops == &semaphore_ops );
+    sem_destroy(sem->p);
+}
+
+static int semaphore_trylock( struct object *obj, struct wait_queue_entry *entry )
+{
+    struct semaphore *sem = (struct semaphore *)obj;
+    assert( obj->ops == &semaphore_ops );
+    if (sem_trywait(sem->p) == -1)
+    {
+        switch(errno) {
+            case EAGAIN:
+                return 0;
+
+            default:
+                perror("sem_trywait");
+                exit(1);
+        }
+    }
+    return 1;
+}
+
+static void semaphore_trylock_undo( struct object *obj, struct wait_queue_entry *entry )
+{
+    struct semaphore *sem = (struct semaphore *)obj;
+    assert( obj->ops == &semaphore_ops );
+    if (sem_post(sem->p) == -1)
+    {
+        perror("sem_post");
+        exit(1);
+    }
+
+}
+
+static int release_semaphore( struct semaphore *sem, unsigned int count,
+                              unsigned int *prev )
+{
+    unsigned int cur_count;
+
+    cur_count = semaphore_get_value(sem);
+
+    if (prev) *prev = cur_count;
+    if (cur_count + count < cur_count || cur_count + count > sem->max)
+    {
+        set_error( STATUS_SEMAPHORE_LIMIT_EXCEEDED );
+        return 0;
+    }
+    else
+    {
+        while(count--)
+        {
+            if (sem_post(sem->p) == -1)
+            {
+                /* FIXME: no atomic way to increase more than once */
+                perror("sem_post");
+            }
+        }
+
+        /* there cannot be any thread to wake up if the count is != 0 */
+        if (!cur_count)
+            wake_up( &sem->obj, count );
+    }
+    return 1;
+}
+
+#endif /* ENABLE_HYBRID_SYNC */
+
+static struct semaphore *create_semaphore( struct directory *root, const struct unicode_str *name,
+                                           unsigned int attr, unsigned int initial, unsigned int max,
+                                           const struct security_descriptor *sd )
+{
+    struct semaphore *sem;
+
+    if (!max || (initial > max))
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return NULL;
+    }
+    if ((sem = create_named_object_dir( root, name, attr, &semaphore_ops )))
+    {
+        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
+        {
+            /* initialize it if it didn't already exist */
+            semaphore_new(sem, initial);
+            sem->max   = max;
+            if (sd) default_set_sd( &sem->obj, sd, OWNER_SECURITY_INFORMATION|
+                                                   GROUP_SECURITY_INFORMATION|
+                                                   DACL_SECURITY_INFORMATION|
+                                                   SACL_SECURITY_INFORMATION );
+        }
+#ifdef ENABLE_HYBRID_SYNC
+        else
+        {
+            assert(sem->p);
+            assert(sem->name[0]);
+        }
+#endif
+    }
+    return sem;
+}
 
 static void semaphore_dump( struct object *obj, int verbose )
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    fprintf( stderr, "Semaphore count=%d max=%d ", sem->count, sem->max );
+    fprintf( stderr, "Semaphore count=%d max=%d ", semaphore_get_value(sem), sem->max );
     dump_object_name( &sem->obj );
     fputc( '\n', stderr );
 }
@@ -140,15 +343,17 @@ static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entr
 {
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    return (sem->count > 0);
+    return (semaphore_get_value(sem) > 0);
 }
 
 static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
+#ifndef ENABLE_HYBRID_SYNC
     struct semaphore *sem = (struct semaphore *)obj;
     assert( obj->ops == &semaphore_ops );
-    assert( sem->count );
-    sem->count--;
+    assert( semaphore_get_value(sem) );
+    semaphore_down(sem);
+#endif
 }
 
 static unsigned int semaphore_map_access( struct object *obj, unsigned int access )
@@ -199,6 +404,10 @@ DECL_HANDLER(create_semaphore)
             reply->handle = alloc_handle( current->process, sem, req->access, req->attributes );
         else
             reply->handle = alloc_handle_no_access_check( current->process, sem, req->access, req->attributes );
+#ifdef ENABLE_HYBRID_SYNC
+        reply->key = sem->key;
+#endif
+        reply->server_ptr = (unsigned long)sem;
         release_object( sem );
     }
 
@@ -219,6 +428,11 @@ DECL_HANDLER(open_semaphore)
     if ((sem = open_object_dir( root, &name, req->attributes, &semaphore_ops )))
     {
         reply->handle = alloc_handle( current->process, &sem->obj, req->access, req->attributes );
+#ifdef ENABLE_HYBRID_SYNC
+        reply->key = sem->key;
+#endif
+        reply->max = sem->max;
+        reply->server_ptr = (unsigned long)sem;
         release_object( sem );
     }
 
@@ -246,7 +460,7 @@ DECL_HANDLER(query_semaphore)
     if ((sem = (struct semaphore *)get_handle_obj( current->process, req->handle,
                                                    SEMAPHORE_QUERY_STATE, &semaphore_ops )))
     {
-        reply->current = sem->count;
+        reply->current = semaphore_get_value(sem);
         reply->max = sem->max;
         release_object( sem );
     }
diff --git a/server/thread.c b/server/thread.c
index 6383000..bb21b93 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -653,6 +653,12 @@ static int check_wait( struct thread *thread )
     int i;
     struct thread_wait *wait = thread->wait;
     struct wait_queue_entry *entry;
+    const int hybrid_sync_enabled =
+#ifdef ENABLE_HYBRID_SYNC
+                                   1;
+#else
+                                   0;
+#endif
 
     assert( wait );
 
@@ -662,24 +668,62 @@ static int check_wait( struct thread *thread )
     /* Suspended threads may not acquire locks, but they can run system APCs */
     if (thread->process->suspend + thread->suspend > 0) return -1;
 
-    if (wait->select == SELECT_WAIT_ALL)
+    if (wait->select == SELECT_WAIT_ALL) /* bWaitAll == TRUE */
     {
         int not_ok = 0;
-        /* Note: we must check them all anyway, as some objects may
-         * want to do something when signaled, even if others are not */
+        int have_hybrid_objects = 0;
+        ULONGLONG undo_list = 0;
+
+        assert(MAXIMUM_WAIT_OBJECTS <= sizeof(ULONGLONG) * 8);
+        /* Note: we must check them all anyway, as some objects (msg queues)
+         * may want to do something when signaled, even if others are not */
         for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
+        {
             not_ok |= !entry->obj->ops->signaled( entry->obj, entry );
+            have_hybrid_objects |= hybrid_sync_enabled && entry->obj->ops->trylock;
+        }
         if (not_ok) goto other_checks;
+
+        if (!have_hybrid_objects)
+            goto do_satisfied;
+
+        /* all objects signaled, try to acquire locks on hybrid objects */
+        for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
+        {
+            if (!entry->obj->ops->trylock)
+                continue;
+
+            /* it's still possible for failure because one of the hybrid objects
+             * may have since been locked */
+            if (entry->obj->ops->trylock( entry->obj, entry ))
+                undo_list |= 1ULL << i;
+            else
+            {
+                /* if a lock cannot be acquired, then undo previously acquired locks */
+                for (--i, --entry; i >= 0; --i, --entry)
+                {
+                    if (undo_list & (1ULL << i))
+                        entry->obj->ops->trylock_undo( entry->obj, entry );
+                }
+                goto other_checks;
+            }
+        }
+
+do_satisfied:
         /* Wait satisfied: tell it to all objects */
         for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
             entry->obj->ops->satisfied( entry->obj, entry );
         return wait->abandoned ? STATUS_ABANDONED_WAIT_0 : STATUS_WAIT_0;
     }
-    else
+    else /* bWaitAll == FALSE */
     {
         for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
         {
-            if (!entry->obj->ops->signaled( entry->obj, entry )) continue;
+            int signaled = hybrid_sync_enabled && entry->obj->ops->trylock
+                         ? entry->obj->ops->trylock( entry->obj, entry )
+                         : entry->obj->ops->signaled( entry->obj, entry );
+
+            if (!signaled) continue;
             /* Wait satisfied: tell it to the object */
             entry->obj->ops->satisfied( entry->obj, entry );
             if (wait->abandoned) i += STATUS_ABANDONED_WAIT_0;
-- 
2.4.6




More information about the wine-devel mailing list