From 599f4653ecd2bea798265f1e342c19bbcbb1162a Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 29 Jul 2018 13:36:42 -0500
Subject: [PATCH 72/83] server: Update the shared memory state when (re)setting
 an event.

---
 server/esync.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 98 insertions(+), 6 deletions(-)

diff --git a/server/esync.c b/server/esync.c
index 847f1e974..1fc9316b1 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -64,6 +64,9 @@ int do_esync(void)
 static char shm_name[29];
 static int shm_fd;
 static off_t shm_size;
+static void **shm_addrs;
+static int shm_addrs_size;  /* length of the allocated shm_addrs array */
+static long pagesize;
 
 static void shm_cleanup(void)
 {
@@ -90,7 +93,12 @@ void esync_init(void)
     if (shm_fd == -1)
         perror( "shm_open" );
 
-    shm_size = sysconf( _SC_PAGESIZE );
+    pagesize = sysconf( _SC_PAGESIZE );
+
+    shm_addrs = calloc( 128, sizeof(shm_addrs[0]) );
+    shm_addrs_size = 128;
+
+    shm_size = pagesize;
     if (ftruncate( shm_fd, shm_size ) == -1)
         perror( "ftruncate" );
 
@@ -199,7 +207,7 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
             while (esync->shm_idx * 8 >= shm_size)
             {
                 /* Better expand the shm section. */
-                shm_size += sysconf( _SC_PAGESIZE );
+                shm_size += pagesize;
                 if (ftruncate( shm_fd, shm_size ) == -1)
                 {
                     fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
@@ -275,24 +283,108 @@ void esync_clear( int fd )
     read( fd, &value, sizeof(value) );
 }
 
+/* Sadly, we need all of this infrastructure to keep the shm state in sync. */
+
+static void *get_shm( unsigned int idx )
+{
+    int entry  = (idx * 8) / pagesize;
+    int offset = (idx * 8) % pagesize;
+
+    if (entry >= shm_addrs_size)
+    {
+        if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
+            fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
+
+        memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
+
+        shm_addrs_size = entry + 1;
+    }
+
+    if (!shm_addrs[entry])
+    {
+        void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
+        if (addr == (void *)-1)
+        {
+            fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
+            perror( "mmap" );
+        }
+
+        if (debug_level)
+            fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
+
+        if (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
+            munmap( addr, pagesize ); /* someone beat us to it */
+    }
+
+    return (void *)((unsigned long)shm_addrs[entry] + offset);
+}
+
+struct event
+{
+    int signaled;
+    int locked;
+};
+C_ASSERT(sizeof(struct event) == 8);
+
+static inline void small_pause(void)
+{
+#ifdef __i386__
+    __asm__ __volatile__( "rep;nop" : : : "memory" );
+#else
+    __asm__ __volatile__( "" : : : "memory" );
+#endif
+}
+
 /* Server-side event support. */
 void esync_set_event( struct esync *esync )
 {
     static const uint64_t value = 1;
+    struct event *event = get_shm( esync->shm_idx );
 
     assert( esync->obj.ops == &esync_ops );
-    if (write( esync->fd, &value, sizeof(value) ) == -1)
-        perror( "esync: write" );
+    assert( event != NULL );
+
+    if (debug_level)
+        fprintf( stderr, "esync_set_event() fd=%d\n", esync->fd );
+
+    /* Acquire the spinlock. */
+    while (interlocked_cmpxchg( &event->locked, 1, 0 ))
+        small_pause();
+
+    if (!interlocked_xchg( &event->signaled, 1 ))
+    {
+        if (write( esync->fd, &value, sizeof(value) ) == -1)
+            perror( "esync: write" );
+    }
+
+    /* Release the spinlock. */
+    event->locked = 0;
 }
 
 void esync_reset_event( struct esync *esync )
 {
     static uint64_t value = 1;
+    struct event *event = get_shm( esync->shm_idx );
 
     assert( esync->obj.ops == &esync_ops );
+    assert( event != NULL );
 
-    /* we don't care about the return value */
-    read( esync->fd, &value, sizeof(value) );
+    if (debug_level)
+        fprintf( stderr, "esync_reset_event() fd=%d\n", esync->fd );
+
+    /* Acquire the spinlock. */
+    while (interlocked_cmpxchg( &event->locked, 1, 0 ))
+        small_pause();
+
+    /* Only bother signaling the fd if we weren't already signaled. */
+    if (interlocked_xchg( &event->signaled, 0 ))
+    {
+        /* we don't care about the return value */
+        read( esync->fd, &value, sizeof(value) );
+    }
+
+    /* Release the spinlock. */
+    event->locked = 0;
 }
 
 DECL_HANDLER(create_esync)
-- 
2.20.1

