>From 81cbdd77ea4e2d0a22b148a9ff2687b70786eae2 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 10 May 2019 00:53:27 -0500
Subject: [PATCH 1/5] ntdll, server: Port to pipes.

Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
---
 dlls/ntdll/esync.c  | 157 ++++++++++++++++++++++-------
 server/console.c    |  10 +-
 server/device.c     |   8 +-
 server/esync.c      | 233 ++++++++++++++++++++++++++++++++------------
 server/esync.h      |  11 ++-
 server/event.c      |   8 +-
 server/fd.c         |  12 +--
 server/file.h       |   2 +-
 server/object.h     |   2 +-
 server/process.c    |   8 +-
 server/process.h    |   2 +-
 server/protocol.def |  10 +-
 server/queue.c      |  10 +-
 server/thread.c     |  13 ++-
 server/thread.h     |   4 +-
 server/timer.c      |   8 +-
 16 files changed, 354 insertions(+), 144 deletions(-)

diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index c2190f2fda0..7a1390c2505 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -53,19 +53,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(esync);
 
 int do_esync(void)
 {
-#ifdef HAVE_SYS_EVENTFD_H
     static int do_esync_cached = -1;
 
     if (do_esync_cached == -1)
         do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
 
     return do_esync_cached;
-#else
-    static int once;
-    if (!once++)
-        FIXME("eventfd not supported on this platform.\n");
-    return 0;
-#endif
 }
 
 /* Entry point for drivers to set queue fd. */
@@ -77,10 +70,81 @@ void __wine_esync_set_queue_fd( int fd )
 struct esync
 {
     enum esync_type type;   /* defined in protocol.def */
+#ifdef HAVE_SYS_EVENTFD_H
     int fd;
+#else
+    int readfd, writefd;
+#endif
     void *shm;              /* pointer to shm section */
 };
 
+/* Emulate read() on an eventfd: clear the object if it's not a semaphore, read
+ * 1 from it if it is. Return a nonzero value if the object was signaled. */
+static ssize_t efd_read(struct esync *esync)
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    uint64_t value;
+
+    return read( esync->fd, &value, sizeof(value) );
+#else
+    static char buffer[4096];
+
+    if (esync->type == ESYNC_SEMAPHORE)
+        return read( esync->readfd, buffer, 1 ) > 0;
+    else
+    {
+        int ret = 0, size;
+        do
+        {
+            size = read( esync->readfd, buffer, sizeof(buffer) );
+            if (size > 0) ret = 1;
+        } while (size == sizeof(buffer));
+        return ret;
+    }
+#endif
+}
+
+/* Emulate write() on an eventfd. Return -1 on failure. */
+static ssize_t efd_write(struct esync *esync, uint64_t value)
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    return write( esync->fd, &value, sizeof(value) );
+#else
+    static const char buffer[4096];
+
+    do
+    {
+        ssize_t ret = write( esync->writefd, buffer, min(value, sizeof(buffer)) );
+        if (ret == -1) return -1;
+        value -= ret;
+    } while (value);
+
+    return 0;
+#endif
+}
+
+static void efd_close(struct esync *esync)
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    close( esync->fd );
+#else
+    close( esync->readfd );
+    close( esync->writefd );
+#endif
+}
+
+/* Careful how this is used—we can't reliably completely clear an object with it. */
+static int get_read_fd( struct esync *obj )
+{
+    if (!obj) return -1;
+
+#ifdef HAVE_SYS_EVENTFD_H
+    return obj->fd;
+#else
+    return obj->readfd;
+#endif
+}
+
 struct semaphore
 {
     int max;
@@ -219,6 +283,34 @@ static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, v
 {
     UINT_PTR entry, idx = handle_to_index( handle, &entry );
 
+#ifndef HAVE_SYS_EVENTFD_H
+    /* We got the read fd, but not the write fd. We only need the latter if
+     * we're not dealing with a server-bound object. */
+
+    int writefd = -1;
+    sigset_t sigset;
+    NTSTATUS ret;
+    obj_handle_t fd_handle;
+
+    if (type != ESYNC_MANUAL_SERVER && type != ESYNC_AUTO_SERVER && type != ESYNC_QUEUE)
+    {
+        server_enter_uninterrupted_section( &fd_cache_section, &sigset );
+
+        SERVER_START_REQ( get_esync_write_fd )
+        {
+            req->handle = wine_server_obj_handle( handle );
+            if (!(ret = wine_server_call( req )))
+            {
+                writefd = receive_fd( &fd_handle );
+                assert( wine_server_ptr_handle(fd_handle) == handle );
+            }
+        }
+        SERVER_END_REQ;
+
+        server_leave_uninterrupted_section( &fd_cache_section, &sigset );
+    }
+#endif
+
     if (entry >= ESYNC_LIST_ENTRIES)
     {
         FIXME( "too many allocated handles, not caching %p\n", handle );
@@ -239,7 +331,12 @@ static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, v
 
     if (!interlocked_cmpxchg((int *)&esync_list[entry][idx].type, type, 0))
     {
+#ifdef HAVE_SYS_EVENTFD_H
         esync_list[entry][idx].fd = fd;
+#else
+        esync_list[entry][idx].readfd = fd;
+        esync_list[entry][idx].writefd = writefd;
+#endif
         esync_list[entry][idx].shm = shm;
     }
     return &esync_list[entry][idx];
@@ -283,7 +380,7 @@ static NTSTATUS get_object( HANDLE handle, struct esync **obj )
     server_enter_uninterrupted_section( &fd_cache_section, &sigset );
     if (!(*obj = get_cached_object( handle )))
     {
-        SERVER_START_REQ( get_esync_fd )
+        SERVER_START_REQ( get_esync_read_fd )
         {
             req->handle = wine_server_obj_handle( handle );
             if (!(ret = wine_server_call( req )))
@@ -327,7 +424,7 @@ NTSTATUS esync_close( HANDLE handle )
     {
         if (interlocked_xchg((int *)&esync_list[entry][idx].type, 0))
         {
-            close( esync_list[entry][idx].fd );
+            efd_close( &esync_list[entry][idx] );
             return STATUS_SUCCESS;
         }
     }
@@ -465,7 +562,7 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
      * write(). The fact that we were able to increase the count means that we
      * have permission to actually write that many releases to the semaphore. */
 
-    if (write( obj->fd, &count64, sizeof(count64) ) == -1)
+    if (efd_write( obj, count64 ) == -1)
         return FILE_GetNtStatus();
 
     return STATUS_SUCCESS;
@@ -580,7 +677,6 @@ static inline void small_pause(void)
 
 NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
 {
-    static const uint64_t value = 1;
     struct esync *obj;
     struct event *event;
     LONG current;
@@ -610,7 +706,7 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
     /* Only bother signaling the fd if we weren't already signaled. */
     if (!(current = interlocked_xchg( &event->signaled, 1 )) || obj->type == ESYNC_AUTO_EVENT)
     {
-        if (write( obj->fd, &value, sizeof(value) ) == -1)
+        if (efd_write( obj, 1 ) == -1)
             ERR("write: %s\n", strerror(errno));
     }
 
@@ -627,7 +723,6 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
 
 NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
 {
-    uint64_t value;
     struct esync *obj;
     struct event *event;
     LONG current;
@@ -653,10 +748,7 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
      * leaving this function, so we always have to read(). */
     if ((current = interlocked_xchg( &event->signaled, 0 )) || obj->type == ESYNC_AUTO_EVENT)
     {
-        if (read( obj->fd, &value, sizeof(value) ) == -1 && errno != EWOULDBLOCK && errno != EAGAIN)
-        {
-            ERR("read: %s\n", strerror(errno));
-        }
+        efd_read( obj );
     }
 
     if (prev) *prev = current;
@@ -672,7 +764,6 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
 
 NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
 {
-    uint64_t value = 1;
     struct esync *obj;
     struct event *event;
     LONG current;
@@ -690,14 +781,14 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
     /* This isn't really correct; an application could miss the write.
      * Unfortunately we can't really do much better. Fortunately this is rarely
      * used (and publicly deprecated). */
-    if (write( obj->fd, &value, sizeof(value) ) == -1)
+    if (efd_write( obj, 1 ) == -1)
         return FILE_GetNtStatus();
 
     /* Try to give other threads a chance to wake up. Hopefully erring on this
      * side is the better thing to do... */
     NtYieldExecution();
 
-    read( obj->fd, &value, sizeof(value) );
+    efd_read( obj );
 
     current = interlocked_xchg( &event->signaled, 0 );
     if (prev) *prev = current;
@@ -726,7 +817,7 @@ NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
 
     if ((ret = get_object( handle, &obj ))) return ret;
 
-    fd.fd = obj->fd;
+    fd.fd = get_read_fd( obj );
     fd.events = POLLIN;
     out->EventState = poll( &fd, 1, 0 );
     out->EventType = (obj->type == ESYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent);
@@ -756,7 +847,6 @@ NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
 {
     struct esync *obj;
     struct mutex *mutex;
-    static const uint64_t value = 1;
     NTSTATUS ret;
 
     TRACE("%p, %p.\n", handle, prev);
@@ -779,7 +869,7 @@ NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
          * theirs. */
         mutex->tid = 0;
 
-        if (write( obj->fd, &value, sizeof(value) ) == -1)
+        if (efd_write( obj, 1 ) == -1)
             return FILE_GetNtStatus();
     }
 
@@ -909,8 +999,6 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
     LARGE_INTEGER now;
     DWORD pollcount;
     ULONGLONG end;
-    int64_t value;
-    ssize_t size;
     int i, j;
     int ret;
 
@@ -1023,7 +1111,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                     }
                     else if (!mutex->count)
                     {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
+                        if (efd_read( obj ))
                         {
                             if (mutex->tid == ~0)
                             {
@@ -1045,7 +1133,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
 
                     if (semaphore->count)
                     {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
+                        if (efd_read( obj ))
                         {
                             TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                             interlocked_xchg_add( &semaphore->count, -1 );
@@ -1060,7 +1148,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
 
                     if (event->signaled)
                     {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
+                        if (efd_read( obj ))
                         {
                             TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                             event->signaled = 0;
@@ -1090,7 +1178,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                 }
             }
 
-            fds[i].fd = obj ? obj->fd : -1;
+            fds[i].fd = get_read_fd( obj );
             fds[i].events = POLLIN;
         }
         if (msgwait)
@@ -1145,7 +1233,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                         else
                         {
                             /* FIXME: Could we check the poll or shm state first? Should we? */
-                            if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value))
+                            if (efd_read( obj ))
                             {
                                 /* We found our object. */
                                 TRACE("Woken up by handle %p [%d].\n", handles[i], i);
@@ -1214,7 +1302,7 @@ tryagain:
             {
                 struct esync *obj = objs[i];
 
-                fds[0].fd = obj ? obj->fd : -1;
+                fds[0].fd = get_read_fd( obj );
 
                 if (obj && obj->type == ESYNC_MUTEX)
                 {
@@ -1253,7 +1341,7 @@ tryagain:
              * handles were signaled. Check to make sure they still are. */
             for (i = 0; i < count; i++)
             {
-                fds[i].fd = objs[i] ? objs[i]->fd : -1;
+                fds[i].fd = get_read_fd( objs[i] );
                 fds[i].events = POLLIN;
             }
             if (msgwait)
@@ -1287,13 +1375,12 @@ tryagain:
                     }
                     case ESYNC_SEMAPHORE:
                     case ESYNC_AUTO_EVENT:
-                        if ((size = read( fds[i].fd, &value, sizeof(value) )) != sizeof(value))
+                        if (!efd_read( obj ))
                         {
                             /* We were too slow. Put everything back. */
-                            value = 1;
                             for (j = i; j >= 0; j--)
                             {
-                                if (write( obj->fd, &value, sizeof(value) ) == -1)
+                                if (efd_write( obj, 1 ) == -1)
                                     return FILE_GetNtStatus();
                             }
 
diff --git a/server/console.c b/server/console.c
index e0e271476d9..f5e1e2f58f9 100644
--- a/server/console.c
+++ b/server/console.c
@@ -97,7 +97,7 @@ static const struct object_ops console_input_ops =
 static void console_input_events_dump( struct object *obj, int verbose );
 static void console_input_events_destroy( struct object *obj );
 static int console_input_events_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int console_input_events_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *console_input_events_get_esync_fd( struct object *obj, enum esync_type *type );
 
 struct console_input_events
 {
@@ -105,7 +105,7 @@ struct console_input_events
     int			  num_alloc;   /* number of allocated events */
     int 		  num_used;    /* number of actually used events */
     struct console_renderer_event*	events;
-    int                   esync_fd;    /* esync file descriptor (signalled when events present) */
+    struct esync_fd      *esync_fd;    /* esync file descriptor (signalled when events present) */
 };
 
 static const struct object_ops console_input_events_ops =
@@ -250,6 +250,8 @@ static void console_input_events_destroy( struct object *obj )
     struct console_input_events *evts = (struct console_input_events *)obj;
     assert( obj->ops == &console_input_events_ops );
     free( evts->events );
+    if (do_esync())
+        esync_close_fd( evts->esync_fd );
 }
 
 /* the renderer events list is signaled when it's not empty */
@@ -260,7 +262,7 @@ static int console_input_events_signaled( struct object *obj, struct wait_queue_
     return (evts->num_used != 0);
 }
 
-static int console_input_events_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *console_input_events_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct console_input_events *evts = (struct console_input_events *)obj;
     *type = ESYNC_MANUAL_SERVER;
@@ -333,7 +335,7 @@ static struct console_input_events *create_console_input_events(void)
     if (!(evt = alloc_object( &console_input_events_ops ))) return NULL;
     evt->num_alloc = evt->num_used = 0;
     evt->events = NULL;
-    evt->esync_fd = -1;
+    evt->esync_fd = NULL;
 
     if (do_esync())
         evt->esync_fd = esync_create_fd( 0, 0 );
diff --git a/server/device.c b/server/device.c
index 8418a9e5ce0..e20a1cac8da 100644
--- a/server/device.c
+++ b/server/device.c
@@ -96,12 +96,12 @@ struct device_manager
     struct list            requests;       /* list of pending irps across all devices */
     struct irp_call       *current_call;   /* call currently executed on client side */
     struct wine_rb_tree    kernel_objects; /* map of objects that have client side pointer associated */
-    int                    esync_fd;       /* esync file descriptor */
+    struct esync_fd       *esync_fd;       /* esync file descriptor */
 };
 
 static void device_manager_dump( struct object *obj, int verbose );
 static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *device_manager_get_esync_fd( struct object *obj, enum esync_type *type );
 static void device_manager_destroy( struct object *obj );
 
 static const struct object_ops device_manager_ops =
@@ -774,7 +774,7 @@ static int device_manager_signaled( struct object *obj, struct wait_queue_entry
     return !list_empty( &manager->requests );
 }
 
-static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *device_manager_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct device_manager *manager = (struct device_manager *)obj;
     *type = ESYNC_MANUAL_SERVER;
@@ -817,7 +817,7 @@ static void device_manager_destroy( struct object *obj )
     }
 
     if (do_esync())
-        close( manager->esync_fd );
+        esync_close_fd( manager->esync_fd );
 }
 
 static struct device_manager *create_device_manager(void)
diff --git a/server/esync.c b/server/esync.c
index 1b035bdb066..030cbd5245a 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -48,16 +48,12 @@
 
 int do_esync(void)
 {
-#ifdef HAVE_SYS_EVENTFD_H
     static int do_esync_cached = -1;
 
     if (do_esync_cached == -1)
         do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
 
     return do_esync_cached;
-#else
-    return 0;
-#endif
 }
 
 static char shm_name[29];
@@ -106,17 +102,29 @@ void esync_init(void)
 
 static struct list mutex_list = LIST_INIT(mutex_list);
 
+struct esync_fd
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    int fd;
+#else
+    /* Write to 0, read from 1. */
+    int fds[2];
+#endif
+};
+
 struct esync
 {
     struct object   obj;            /* object header */
-    int             fd;             /* eventfd file descriptor */
+    /* No, this isn't a pointer. We can have lots of these so let's try to save
+     * memory a little. */
+    struct esync_fd fd;             /* eventfd file descriptor */
     enum esync_type type;
     unsigned int    shm_idx;        /* index into the shared memory section */
     struct list     mutex_entry;    /* entry in the mutex list (if applicable) */
 };
 
 static void esync_dump( struct object *obj, int verbose );
-static int esync_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *esync_get_esync_fd( struct object *obj, enum esync_type *type );
 static unsigned int esync_map_access( struct object *obj, unsigned int access );
 static void esync_destroy( struct object *obj );
 
@@ -148,14 +156,18 @@ static void esync_dump( struct object *obj, int verbose )
 {
     struct esync *esync = (struct esync *)obj;
     assert( obj->ops == &esync_ops );
-    fprintf( stderr, "esync fd=%d\n", esync->fd );
+#ifdef HAVE_SYS_EVENTFD_H
+    fprintf( stderr, "esync type=%d fd=%d\n", esync->type, esync->fd.fd );
+#else
+    fprintf( stderr, "esync type=%d fd=%d,%d\n", esync->type, esync->fd.fds[0], esync->fd.fds[1] );
+#endif
 }
 
-static int esync_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *esync_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct esync *esync = (struct esync *)obj;
     *type = esync->type;
-    return esync->fd;
+    return &esync->fd;
 }
 
 static unsigned int esync_map_access( struct object *obj, unsigned int access )
@@ -173,7 +185,12 @@ static void esync_destroy( struct object *obj )
     struct esync *esync = (struct esync *)obj;
     if (esync->type == ESYNC_MUTEX)
         list_remove( &esync->mutex_entry );
-    close( esync->fd );
+#ifdef HAVE_SYS_EVENTFD_H
+    close( esync->fd.fd );
+#else
+    close( esync->fd.fds[0] );
+    close( esync->fd.fds[1] );
+#endif
 }
 
 static int type_matches( enum esync_type type1, enum esync_type type2 )
@@ -238,36 +255,71 @@ struct event
 };
 C_ASSERT(sizeof(struct event) == 8);
 
+static int esync_init_fd( struct esync_fd *fd, int initval, int semaphore )
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    int flags = EFD_CLOEXEC | EFD_NONBLOCK;
+    if (semaphore) flags |= EFD_SEMAPHORE;
+    fd->fd = eventfd( initval, flags );
+    if (fd->fd == -1)
+    {
+        perror( "eventfd" );
+        return -1;
+    }
+#else
+    static const unsigned char value;
+    int fdflags;
+
+    if (pipe(fd->fds) == -1)
+    {
+        perror( "pipe" );
+        return -1;
+    }
+    fcntl( fd->fds[0], F_SETFD, FD_CLOEXEC );
+    if ((fdflags = fcntl( fd->fds[0], F_GETFL, 0 )) == -1)
+        fdflags = 0;
+    fcntl( fd->fds[0], F_SETFL, fdflags | O_NONBLOCK );
+
+    fcntl( fd->fds[1], F_SETFD, FD_CLOEXEC );
+    if ((fdflags = fcntl( fd->fds[1], F_GETFL, 0 )) == -1)
+        fdflags = 0;
+    fcntl( fd->fds[1], F_SETFL, fdflags | O_NONBLOCK );
+
+    while (initval--)
+        write( fd->fds[1], &value, sizeof(value) );
+#endif
+    return 0;
+}
+
 static struct esync *create_esync( struct object *root, const struct unicode_str *name,
     unsigned int attr, int initval, int max, enum esync_type type,
     const struct security_descriptor *sd )
 {
-#ifdef HAVE_SYS_EVENTFD_H
     struct esync *esync;
 
     if ((esync = create_named_object( root, &esync_ops, name, attr, sd )))
     {
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
-            int flags = EFD_CLOEXEC | EFD_NONBLOCK;
-
-            if (type == ESYNC_SEMAPHORE)
-                flags |= EFD_SEMAPHORE;
+            esync->type = type;
 
             /* initialize it if it didn't already exist */
-            esync->fd = eventfd( initval, flags );
-            if (esync->fd == -1)
+            if (esync_init_fd( &esync->fd, initval, type == ESYNC_SEMAPHORE ) == -1)
             {
                 perror( "eventfd" );
                 file_set_error();
                 release_object( esync );
                 return NULL;
             }
-            esync->type = type;
 
             /* Use the fd as index, since that'll be unique across all
              * processes, but should hopefully end up also allowing reuse. */
-            esync->shm_idx = esync->fd + 1; /* we keep index 0 reserved */
+#ifdef HAVE_SYS_EVENTFD_H
+            esync->shm_idx = esync->fd.fd + 1; /* we keep index 0 reserved */
+#else
+            esync->shm_idx = esync->fd.fds[0] + 1; /* we keep index 0 reserved */
+#endif
+
             while (esync->shm_idx * 8 >= shm_size)
             {
                 /* Better expand the shm section. */
@@ -325,59 +377,66 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
         }
     }
     return esync;
-#else
-    /* FIXME: Provide a fallback implementation using pipe(). */
-    set_error( STATUS_NOT_IMPLEMENTED );
-    return NULL;
-#endif
 }
 
-/* Create a file descriptor for an existing handle.
- * Caller must close the handle when it's done; it's not linked to an esync
- * server object in any way. */
-int esync_create_fd( int initval, int flags )
+/* Create an eventfd or eventfd-like primitive for an existing handle.
+ * Caller must close with esync_close_fd(). */
+struct esync_fd *esync_create_fd( int initval, int semaphore )
 {
-#ifdef HAVE_SYS_EVENTFD_H
-    int fd;
-
-    fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK );
-    if (fd == -1)
-        perror( "eventfd" );
-
+    struct esync_fd *fd = mem_alloc( sizeof(*fd) );
+    esync_init_fd( fd, initval, semaphore );
     return fd;
+}
+
+void esync_close_fd( struct esync_fd *fd )
+{
+#ifdef HAVE_SYS_EVENTFD_H
+    close( fd->fd );
 #else
-    return -1;
+    close( fd->fds[0] );
+    close( fd->fds[1] );
 #endif
+    free( fd );
 }
 
 /* Wake up a specific fd. */
-void esync_wake_fd( int fd )
+void esync_wake_fd( struct esync_fd *fd )
 {
+#ifdef HAVE_SYS_EVENTFD_H
     static const uint64_t value = 1;
 
-    if (write( fd, &value, sizeof(value) ) == -1)
+    if (write( fd->fd, &value, sizeof(value) ) == -1)
+        perror( "esync: write" );
+#else
+    static const char value;
+
+    if (write( fd->fds[1], &value, sizeof(value) ) == -1)
         perror( "esync: write" );
+#endif
 }
 
 /* Wake up a server-side esync object. */
 void esync_wake_up( struct object *obj )
 {
     enum esync_type dummy;
-    int fd;
 
     if (obj->ops->get_esync_fd)
     {
-        fd = obj->ops->get_esync_fd( obj, &dummy );
-        esync_wake_fd( fd );
+        esync_wake_fd( obj->ops->get_esync_fd( obj, &dummy ) );
     }
 }
 
-void esync_clear( int fd )
+void esync_clear( struct esync_fd *fd )
 {
+#ifdef HAVE_SYS_EVENTFD_H
     uint64_t value;
 
     /* we don't care about the return value */
-    read( fd, &value, sizeof(value) );
+    read( fd->fd, &value, sizeof(value) );
+#else
+    static char buffer[4096];
+    while (read( fd->fds[0], buffer, sizeof(buffer) ) == sizeof(buffer));
+#endif
 }
 
 static inline void small_pause(void)
@@ -392,14 +451,17 @@ static inline void small_pause(void)
 /* 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 );
     assert( event != NULL );
 
     if (debug_level)
-        fprintf( stderr, "esync_set_event() fd=%d\n", esync->fd );
+    {
+        fprintf( stderr, "esync_set_event()" );
+        esync_dump( &esync->obj, 0 );
+        fprintf( stderr, "\n" );
+    }
 
     if (esync->type == ESYNC_MANUAL_EVENT)
     {
@@ -409,10 +471,7 @@ void esync_set_event( struct esync *esync )
     }
 
     if (!interlocked_xchg( &event->signaled, 1 ))
-    {
-        if (write( esync->fd, &value, sizeof(value) ) == -1)
-            perror( "esync: write" );
-    }
+        esync_wake_fd( &esync->fd );
 
     if (esync->type == ESYNC_MANUAL_EVENT)
     {
@@ -423,14 +482,17 @@ void esync_set_event( struct esync *esync )
 
 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 );
 
     if (debug_level)
-        fprintf( stderr, "esync_reset_event() fd=%d\n", esync->fd );
+    {
+        fprintf( stderr, "esync_reset_event()" );
+        esync_dump( &esync->obj, 0 );
+        fprintf( stderr, "\n" );
+    }
 
     if (esync->type == ESYNC_MANUAL_EVENT)
     {
@@ -441,10 +503,7 @@ void esync_reset_event( struct esync *esync )
 
     /* 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) );
-    }
+        esync_clear( &esync->fd );
 
     if (esync->type == ESYNC_MANUAL_EVENT)
     {
@@ -455,7 +514,6 @@ void esync_reset_event( struct esync *esync )
 
 void esync_abandon_mutexes( struct thread *thread )
 {
-    unsigned int index = 0;
     struct esync *esync;
 
     LIST_FOR_EACH_ENTRY( esync, &mutex_list, struct esync, mutex_entry )
@@ -465,10 +523,14 @@ void esync_abandon_mutexes( struct thread *thread )
         if (mutex->tid == thread->id)
         {
             if (debug_level)
-                fprintf( stderr, "esync_abandon_mutexes() fd=%d\n", esync->fd );
+            {
+                fprintf( stderr, "esync_abandon_mutexes()\n" );
+                esync_dump( &esync->obj, 0 );
+                fprintf( stderr, "\n" );
+            }
             mutex->tid = ~0;
             mutex->count = 0;
-            esync_wake_fd( esync->fd );
+            esync_wake_fd( &esync->fd );
         }
     }
 }
@@ -506,7 +568,11 @@ DECL_HANDLER(create_esync)
 
         reply->type = esync->type;
         reply->shm_idx = esync->shm_idx;
-        send_client_fd( current->process, esync->fd, reply->handle );
+#ifdef HAVE_SYS_EVENTFD_H
+        send_client_fd( current->process, esync->fd.fd, reply->handle );
+#else
+        send_client_fd( current->process, esync->fd.fds[0], reply->handle );
+#endif
         release_object( esync );
     }
 
@@ -539,18 +605,22 @@ DECL_HANDLER(open_esync)
         reply->type = esync->type;
         reply->shm_idx = esync->shm_idx;
 
-        send_client_fd( current->process, esync->fd, reply->handle );
+#ifdef HAVE_SYS_EVENTFD_H
+        send_client_fd( current->process, esync->fd.fd, reply->handle );
+#else
+        send_client_fd( current->process, esync->fd.fds[0], reply->handle );
+#endif
         release_object( esync );
     }
 }
 
 /* Retrieve a file descriptor for an esync object which will be signaled by the
  * server. The client should only read from (i.e. wait on) this object. */
-DECL_HANDLER(get_esync_fd)
+DECL_HANDLER(get_esync_read_fd)
 {
     struct object *obj;
     enum esync_type type;
-    int fd;
+    struct esync_fd *fd;
 
     if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL )))
         return;
@@ -566,7 +636,11 @@ DECL_HANDLER(get_esync_fd)
         }
         else
             reply->shm_idx = 0;
-        send_client_fd( current->process, fd, req->handle );
+#ifdef HAVE_SYS_EVENTFD_H
+        send_client_fd( current->process, fd->fd, req->handle );
+#else
+        send_client_fd( current->process, fd->fds[0], req->handle );
+#endif
     }
     else
     {
@@ -581,8 +655,39 @@ DECL_HANDLER(get_esync_fd)
     release_object( obj );
 }
 
+/* Retrieve a file descriptor for an esync object, which will be written to.
+ * This should not be used for server-bound objects. */
+DECL_HANDLER(get_esync_write_fd)
+{
+    struct object *obj;
+    enum esync_type type;
+    struct esync_fd *fd;
+
+    if (!(obj = get_handle_obj( current->process, req->handle, SYNCHRONIZE, NULL )))
+        return;
+
+    if (obj->ops->get_esync_fd)
+    {
+        fd = obj->ops->get_esync_fd( obj, &type );
+#ifdef HAVE_SYS_EVENTFD_H
+        fprintf( stderr, "This path shouldn't be reached! ntdll is being stupid.\n" );
+        send_client_fd( current->process, fd->fd, req->handle );
+#else
+        send_client_fd( current->process, fd->fds[1], req->handle );
+#endif
+    }
+    else
+        set_error( STATUS_NOT_IMPLEMENTED );
+
+    release_object( obj );
+}
+
 /* Return the fd used for waiting on user APCs. */
 DECL_HANDLER(get_esync_apc_fd)
 {
-    send_client_fd( current->process, current->esync_apc_fd, current->id );
+#ifdef HAVE_SYS_EVENTFD_H
+    send_client_fd( current->process, current->esync_apc_fd->fd, current->id );
+#else
+    send_client_fd( current->process, current->esync_apc_fd->fds[0], current->id );
+#endif
 }
diff --git a/server/esync.h b/server/esync.h
index 125da8e9d12..2e8fc2714e6 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -20,10 +20,15 @@
 
 extern int do_esync(void);
 void esync_init(void);
-int esync_create_fd( int initval, int flags );
-void esync_wake_fd( int fd );
+
+/* This is an eventfd or something that behaves like one. */
+struct esync_fd;
+
+struct esync_fd *esync_create_fd( int initval, int semaphore );
+void esync_close_fd( struct esync_fd *fd );
+void esync_wake_fd( struct esync_fd *fd );
 void esync_wake_up( struct object *obj );
-void esync_clear( int fd );
+void esync_clear( struct esync_fd *fd );
 
 struct esync;
 
diff --git a/server/event.c b/server/event.c
index 79287e7edc0..4c352e80de9 100644
--- a/server/event.c
+++ b/server/event.c
@@ -43,14 +43,14 @@ struct event
     struct list    kernel_object;   /* list of kernel object pointers */
     int            manual_reset;    /* is it a manual reset event? */
     int            signaled;        /* event has been signaled */
-    int            esync_fd;        /* esync file descriptor */
+    struct esync_fd *esync_fd;        /* esync file descriptor */
 };
 
 static void event_dump( struct object *obj, int verbose );
 static struct object_type *event_get_type( struct object *obj );
 static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
-static int event_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *event_get_esync_fd( struct object *obj, enum esync_type *type );
 static unsigned int event_map_access( struct object *obj, unsigned int access );
 static int event_signal( struct object *obj, unsigned int access);
 static struct list *event_get_kernel_obj_list( struct object *obj );
@@ -203,7 +203,7 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry )
     return event->signaled;
 }
 
-static int event_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *event_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct event *event = (struct event *)obj;
     *type = ESYNC_MANUAL_SERVER;    /* all server-created events are manual-reset */
@@ -252,7 +252,7 @@ static void event_destroy( struct object *obj )
     struct event *event = (struct event *)obj;
 
     if (do_esync())
-        close( event->esync_fd );
+        esync_close_fd( event->esync_fd );
 }
 
 struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name,
diff --git a/server/fd.c b/server/fd.c
index 48fa94596bb..ad128f52de6 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -196,7 +196,7 @@ struct fd
     struct completion   *completion;  /* completion object attached to this fd */
     apc_param_t          comp_key;    /* completion key to set in completion events */
     unsigned int         comp_flags;  /* completion flags */
-    int                  esync_fd;    /* esync file descriptor */
+    struct esync_fd     *esync_fd;    /* esync file descriptor */
 };
 
 static void fd_dump( struct object *obj, int verbose );
@@ -1504,7 +1504,7 @@ static void fd_destroy( struct object *obj )
     }
 
     if (do_esync())
-        close( fd->esync_fd );
+        esync_close_fd( fd->esync_fd );
 }
 
 /* check if the desired access is possible without violating */
@@ -1619,7 +1619,7 @@ static struct fd *alloc_fd_object(void)
     fd->poll_index = -1;
     fd->completion = NULL;
     fd->comp_flags = 0;
-    fd->esync_fd   = -1;
+    fd->esync_fd   = NULL;
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
     init_async_queue( &fd->wait_q );
@@ -1660,7 +1660,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
     fd->completion = NULL;
     fd->comp_flags = 0;
     fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
-    fd->esync_fd   = -1;
+    fd->esync_fd   = NULL;
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
     init_async_queue( &fd->wait_q );
@@ -2028,10 +2028,10 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry )
     return ret;
 }
 
-int default_fd_get_esync_fd( struct object *obj, enum esync_type *type )
+struct esync_fd *default_fd_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct fd *fd = get_obj_fd( obj );
-    int ret = fd->esync_fd;
+    struct esync_fd *ret = fd->esync_fd;
     *type = ESYNC_MANUAL_SERVER;
     release_object( fd );
     return ret;
diff --git a/server/file.h b/server/file.h
index c9e30c360a5..1a1f97c06a5 100644
--- a/server/file.h
+++ b/server/file.h
@@ -103,7 +103,7 @@ extern int is_fd_signaled( struct fd *fd );
 extern char *dup_fd_name( struct fd *root, const char *name );
 
 extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry );
-extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type );
+extern struct esync_fd *default_fd_get_esync_fd( struct object *obj, enum esync_type *type );
 extern unsigned int default_fd_map_access( struct object *obj, unsigned int access );
 extern int default_fd_get_poll_events( struct fd *fd );
 extern void default_poll_event( struct fd *fd, int event );
diff --git a/server/object.h b/server/object.h
index 5c26188e489..af8739baffd 100644
--- a/server/object.h
+++ b/server/object.h
@@ -70,7 +70,7 @@ struct object_ops
     /* is object signaled? */
     int  (*signaled)(struct object *,struct wait_queue_entry *);
     /* return the esync fd for this object */
-    int (*get_esync_fd)(struct object *, enum esync_type *type);
+    struct esync_fd *(*get_esync_fd)(struct object *, enum esync_type *type);
     /* wait satisfied */
     void (*satisfied)(struct object *,struct wait_queue_entry *);
     /* signal an object */
diff --git a/server/process.c b/server/process.c
index 2e8911548f2..5f8cbb8afb9 100644
--- a/server/process.c
+++ b/server/process.c
@@ -68,7 +68,7 @@ static struct security_descriptor *process_get_sd( struct object *obj );
 static void process_poll_event( struct fd *fd, int event );
 static struct list *process_get_kernel_obj_list( struct object *obj );
 static void process_destroy( struct object *obj );
-static int process_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *process_get_esync_fd( struct object *obj, enum esync_type *type );
 static void terminate_process( struct process *process, struct thread *skip, int exit_code );
 
 static const struct object_ops process_ops =
@@ -594,7 +594,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all,
     process->trace_data      = 0;
     process->rawinput_mouse  = NULL;
     process->rawinput_kbd    = NULL;
-    process->esync_fd        = -1;
+    process->esync_fd        = NULL;
     list_init( &process->kernel_object );
     list_init( &process->thread_list );
     list_init( &process->locks );
@@ -699,7 +699,7 @@ static void process_destroy( struct object *obj )
     free( process->dir_cache );
 
     if (do_esync())
-        close( process->esync_fd );
+        esync_close_fd( process->esync_fd );
 }
 
 /* dump a process on stdout for debugging purposes */
@@ -724,7 +724,7 @@ static int process_signaled( struct object *obj, struct wait_queue_entry *entry
     return !process->running_threads;
 }
 
-static int process_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *process_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct process *process = (struct process *)obj;
     *type = ESYNC_MANUAL_SERVER;
diff --git a/server/process.h b/server/process.h
index 7cb41ecfbc0..2b0a993fc6d 100644
--- a/server/process.h
+++ b/server/process.h
@@ -99,7 +99,7 @@ struct process
     const struct rawinput_device *rawinput_mouse; /* rawinput mouse device, if any */
     const struct rawinput_device *rawinput_kbd;   /* rawinput keyboard device, if any */
     struct list          kernel_object;   /* list of kernel object pointers */
-    int                  esync_fd;        /* esync file descriptor (signaled on exit) */
+    struct esync_fd     *esync_fd;        /* esync file descriptor (signaled on exit) */
 };
 
 struct process_snapshot
diff --git a/server/protocol.def b/server/protocol.def
index 682ecd4b357..f2b720c71a6 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4008,14 +4008,20 @@ struct handle_info
     unsigned int shm_idx;       /* this object's index into the shm section */
 @END
 
-/* Retrieve the esync fd for an object. */
-@REQ(get_esync_fd)
+/* Retrieve the esync read fd for an object. */
+@REQ(get_esync_read_fd)
     obj_handle_t handle;        /* handle to the object */
 @REPLY
     int          type;          /* esync type (defined below) */
     unsigned int shm_idx;       /* this object's index into the shm section */
 @END
 
+/* Retrieve the esync write fd for an object. */
+@REQ(get_esync_write_fd)
+    obj_handle_t handle;        /* handle to the object */
+@REPLY
+@END
+
 /* Retrieve the fd to wait on for user APCs. */
 @REQ(get_esync_apc_fd)
 @END
diff --git a/server/queue.c b/server/queue.c
index 6690b68bb68..8c153da457b 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -121,7 +121,7 @@ struct msg_queue
 {
     struct object          obj;             /* object header */
     struct fd             *fd;              /* optional file descriptor to poll */
-    int                    esync_fd;        /* esync file descriptor (signalled on message) */
+    struct esync_fd       *esync_fd;        /* esync file descriptor (signalled on message) */
     int                    esync_in_msgwait; /* our thread is currently waiting on us */
     unsigned int           wake_bits;       /* wakeup bits */
     unsigned int           wake_mask;       /* wakeup mask */
@@ -167,7 +167,7 @@ static void msg_queue_dump( struct object *obj, int verbose );
 static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry );
 static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
 static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *msg_queue_get_esync_fd( struct object *obj, enum esync_type *type );
 static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static void msg_queue_destroy( struct object *obj );
 static void msg_queue_poll_event( struct fd *fd, int event );
@@ -300,7 +300,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
     if ((queue = alloc_object( &msg_queue_ops )))
     {
         queue->fd              = NULL;
-        queue->esync_fd        = -1;
+        queue->esync_fd        = NULL;
         queue->wake_bits       = 0;
         queue->wake_mask       = 0;
         queue->changed_bits    = 0;
@@ -1003,7 +1003,7 @@ static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entr
     return ret || is_signaled( queue );
 }
 
-static int msg_queue_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *msg_queue_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct msg_queue *queue = (struct msg_queue *)obj;
     *type = ESYNC_QUEUE;
@@ -1056,7 +1056,7 @@ static void msg_queue_destroy( struct object *obj )
     if (queue->fd) release_object( queue->fd );
 
     if (do_esync())
-        close( queue->esync_fd );
+        esync_close_fd( queue->esync_fd );
 }
 
 static void msg_queue_poll_event( struct fd *fd, int event )
diff --git a/server/thread.c b/server/thread.c
index b757862b01f..6225f151bee 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -133,7 +133,7 @@ static const struct object_ops thread_apc_ops =
 static void dump_thread( struct object *obj, int verbose );
 static struct object_type *thread_get_type( struct object *obj );
 static int thread_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int thread_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *thread_get_esync_fd( struct object *obj, enum esync_type *type );
 static unsigned int thread_map_access( struct object *obj, unsigned int access );
 static void thread_poll_event( struct fd *fd, int event );
 static struct list *thread_get_kernel_obj_list( struct object *obj );
@@ -187,8 +187,8 @@ static inline void init_thread_structure( struct thread *thread )
     thread->suspend_context = NULL;
     thread->teb             = 0;
     thread->entry_point     = 0;
-    thread->esync_fd        = -1;
-    thread->esync_apc_fd    = -1;
+    thread->esync_fd        = NULL;
+    thread->esync_apc_fd    = NULL;
     thread->debug_ctx       = NULL;
     thread->system_regs     = 0;
     thread->queue           = NULL;
@@ -377,7 +377,10 @@ static void destroy_thread( struct object *obj )
     if (thread->token) release_object( thread->token );
 
     if (do_esync())
-        close( thread->esync_fd );
+    {
+        esync_close_fd( thread->esync_fd );
+        esync_close_fd( thread->esync_apc_fd );
+    }
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -403,7 +406,7 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
     return (mythread->state == TERMINATED);
 }
 
-static int thread_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *thread_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct thread *thread = (struct thread *)obj;
     *type = ESYNC_MANUAL_SERVER;
diff --git a/server/thread.h b/server/thread.h
index 959cec504b5..e28a9c47e74 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -54,8 +54,8 @@ struct thread
     struct process        *process;
     thread_id_t            id;            /* thread id */
     struct list            mutex_list;    /* list of currently owned mutexes */
-    int                    esync_fd;      /* esync file descriptor (signalled on exit) */
-    int                    esync_apc_fd;  /* esync apc fd (signalled when APCs are present) */
+    struct esync_fd       *esync_fd;      /* esync file descriptor (signalled on exit) */
+    struct esync_fd       *esync_apc_fd;  /* esync apc fd (signalled when APCs are present) */
     struct debug_ctx      *debug_ctx;     /* debugger context if this thread is a debugger */
     unsigned int           system_regs;   /* which system regs have been set */
     struct msg_queue      *queue;         /* message queue */
diff --git a/server/timer.c b/server/timer.c
index f2403fc9bac..4de9fdccf86 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -49,13 +49,13 @@ struct timer
     struct thread       *thread;    /* thread that set the APC function */
     client_ptr_t         callback;  /* callback APC function */
     client_ptr_t         arg;       /* callback argument */
-    int                  esync_fd;  /* esync file descriptor */
+    struct esync_fd     *esync_fd;  /* esync file descriptor */
 };
 
 static void timer_dump( struct object *obj, int verbose );
 static struct object_type *timer_get_type( struct object *obj );
 static int timer_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int timer_get_esync_fd( struct object *obj, enum esync_type *type );
+static struct esync_fd *timer_get_esync_fd( struct object *obj, enum esync_type *type );
 static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static unsigned int timer_map_access( struct object *obj, unsigned int access );
 static void timer_destroy( struct object *obj );
@@ -212,7 +212,7 @@ static int timer_signaled( struct object *obj, struct wait_queue_entry *entry )
     return timer->signaled;
 }
 
-static int timer_get_esync_fd( struct object *obj, enum esync_type *type )
+static struct esync_fd *timer_get_esync_fd( struct object *obj, enum esync_type *type )
 {
     struct timer *timer = (struct timer *)obj;
     *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER;
@@ -242,6 +242,8 @@ static void timer_destroy( struct object *obj )
 
     if (timer->timeout) remove_timeout_user( timer->timeout );
     if (timer->thread) release_object( timer->thread );
+    if (do_esync())
+        esync_close_fd( timer->esync_fd );
 }
 
 /* create a timer */
-- 
2.25.1

