File ntsync7-staging-rebase.patch of Package wine

From 688c81b3ef10c8df31a3d6629e74ea0c5a906435 Mon Sep 17 00:00:00 2001
From: v-fox <virtuousfox@gmail.com>
Date: Sun, 9 Mar 2025 17:48:13 +0500
Subject: [PATCH] ntsync7 staging rebase

---
 README.esync                   |  196 -----
 configure                      |    6 +
 configure.ac                   |    9 +-
 dlls/kernel32/tests/sync.c     |  462 +----------
 dlls/ntdll/Makefile.in         |    1 -
 dlls/ntdll/unix/esync.c        | 1325 --------------------------------
 dlls/ntdll/unix/esync.h        |   61 --
 dlls/ntdll/unix/file.c         |    2 +-
 dlls/ntdll/unix/loader.c       |    2 -
 dlls/ntdll/unix/process.c      |    2 +-
 dlls/ntdll/unix/server.c       |   32 +-
 dlls/ntdll/unix/sync.c         | 1019 ++++++++++++++++++++++--
 dlls/ntdll/unix/thread.c       |    2 +-
 dlls/ntdll/unix/unix_private.h |   11 +-
 dlls/ntdll/unix/virtual.c      |    2 +-
 include/config.h.in            |    6 +-
 include/wine/server_protocol.h |  104 ++-
 server/Makefile.in             |    2 +-
 server/async.c                 |    4 +-
 server/atom.c                  |    2 +-
 server/change.c                |    2 +-
 server/clipboard.c             |    2 +-
 server/completion.c            |   27 +-
 server/console.c               |  127 ++-
 server/debugger.c              |   25 +-
 server/device.c                |   49 +-
 server/directory.c             |    4 +-
 server/esync.c                 |  588 --------------
 server/esync.h                 |   35 -
 server/event.c                 |   83 +-
 server/fd.c                    |   48 +-
 server/file.c                  |    2 +-
 server/file.h                  |    2 +-
 server/handle.c                |    2 +-
 server/hook.c                  |    2 +-
 server/inproc_sync.c           |  418 ++++++++++
 server/mailslot.c              |    8 +-
 server/main.c                  |    4 -
 server/mapping.c               |    6 +-
 server/mutex.c                 |   46 +-
 server/named_pipe.c            |   12 +-
 server/object.c                |   10 +-
 server/object.h                |   16 +-
 server/process.c               |   51 +-
 server/process.h               |    2 +-
 server/protocol.def            |   70 +-
 server/queue.c                 |  135 ++--
 server/registry.c              |    2 +-
 server/request.c               |    2 +-
 server/request_handlers.h      |   61 +-
 server/request_trace.h         |   71 +-
 server/semaphore.c             |   29 +-
 server/serial.c                |    2 +-
 server/signal.c                |    2 +-
 server/sock.c                  |    6 +-
 server/symlink.c               |    2 +-
 server/thread.c                |  101 +--
 server/thread.h                |    5 +-
 server/timer.c                 |   37 +-
 server/token.c                 |    2 +-
 server/window.c                |    2 +-
 server/winstation.c            |    4 +-
 62 files changed, 2116 insertions(+), 3238 deletions(-)
 delete mode 100644 README.esync
 delete mode 100644 dlls/ntdll/unix/esync.c
 delete mode 100644 dlls/ntdll/unix/esync.h
 delete mode 100644 server/esync.c
 delete mode 100644 server/esync.h
 create mode 100644 server/inproc_sync.c

diff --git a/README.esync b/README.esync
deleted file mode 100644
index 11d8656..0000000
--- a/README.esync
+++ /dev/null
@@ -1,196 +0,0 @@
-This is eventfd-based synchronization, or 'esync' for short. Turn it on with
-WINEESYNC=1; debug it with +esync.
-
-== BUGS AND LIMITATIONS ==
-
-Please let me know if you find any bugs. If you can, also attach a log with
-+seh,+pid,+esync,+server,+timestamp.
-
-If you get something like "eventfd: Too many open files" and then things start
-crashing, you've probably run out of file descriptors. esync creates one
-eventfd descriptor for each synchronization object, and some games may use a
-large number of these.  Linux by default limits a process to 4096 file
-descriptors, which probably was reasonable back in the nineties but isn't
-really anymore. (Fortunately Debian and derivatives [Ubuntu, Mint] already
-have a reasonable limit.) To raise the limit you'll want to edit
-/etc/security/limits.conf and add a line like
-
-* hard nofile 1048576
-
-then restart your session.
-
-On distributions using systemd, the settings in `/etc/security/limits.conf`
-will be overridden by systemd's own settings. If you run `ulimit -Hn` and it
-returns a lower number than the one you've previously set, then you can set
-
-DefaultLimitNOFILE=1048576
-
-in both `/etc/systemd/system.conf` and `/etc/systemd/user.conf`. You can then
-execute `sudo systemctl daemon-reexec` and restart your session. Check again
-with `ulimit -Hn` that the limit is correct.
-
-Also note that if the wineserver has esync active, all clients also must, and
-vice versa. Otherwise things will probably crash quite badly.
-
-== EXPLANATION ==
-
-The aim is to execute all synchronization operations in "user-space", that is,
-without going through wineserver. We do this using Linux's eventfd
-facility. The main impetus to using eventfd is so that we can poll multiple
-objects at once; in particular we can't do this with futexes, or pthread
-semaphores, or the like. The only way I know of to wait on any of multiple
-objects is to use select/poll/epoll to wait on multiple fds, and eventfd gives
-us those fds in a quite usable way.
-
-Whenever a semaphore, event, or mutex is created, we have the server, instead
-of creating a traditional server-side event/semaphore/mutex, instead create an
-'esync' primitive. These live in esync.c and are very slim objects; in fact,
-they don't even know what type of primitive they are. The server is involved
-at all because we still need a way of creating named objects, passing handles
-to another process, etc.
-
-The server creates an eventfd file descriptor with the requested parameters
-and passes it back to ntdll. ntdll creates an object of the appropriate type,
-then caches it in a table. This table is copied almost wholesale from the fd
-cache code in server.c.
-
-Specific operations follow quite straightforwardly from eventfd:
-
-* To release an object, or set an event, we simply write() to it.
-* An object is signalled if read() succeeds on it. Notably, we create all
-  eventfd descriptors with O_NONBLOCK, so that we can atomically check if an
-  object is signalled and grab it if it is. This also lets us reset events.
-* For objects whose state should not be reset upon waiting—e.g. manual-reset
-  events—we simply check for the POLLIN flag instead of reading.
-* Semaphores are handled by the EFD_SEMAPHORE flag. This matches up quite well
-  (although with some difficulties; see below).
-* Mutexes store their owner thread locally. This isn't reliable information if
-  a different process's thread owns the mutex, but this doesn't matter—a
-  thread should only care whether it owns the mutex, so it knows whether to
-  try waiting on it or simply to increase the recursion count.
-
-The interesting part about esync is that (almost) all waits happen in ntdll,
-including those on server-bound objects. The idea here is that on the server
-side, for any waitable object, we create an eventfd file descriptor (not an
-esync primitive), and then pass it to ntdll if the program tries to wait on
-it. These are cached too, so only the first wait will require a round trip to
-the server. Then the server signals the file descriptor as appropriate, and
-thereby wakes up the client. So far this is implemented for processes,
-threads, message queues (difficult; see below), and device managers (necessary
-for drivers to work). All of these are necessarily server-bound, so we
-wouldn't really gain anything by signalling on the client side instead. Of
-course, except possibly for message queues, it's not likely that any program
-(cutting-edge D3D game or not) is going to be causing a great wineserver load
-by waiting on any of these objects; the motivation was rather to provide a way
-to wait on ntdll-bound and server-bound objects at the same time.
-
-Some cases are still passed to the server, and there's probably no reason not
-to keep them that way. Those that I noticed while testing include: async
-objects, which are internal to the file APIs and never exposed to userspace,
-startup_info objects, which are internal to the loader and signalled when a
-process starts, and keyed events, which are exposed through an ntdll API
-(although not through kernel32) but can't be mixed with other objects (you
-have to use NtWaitForKeyedEvent()). Other cases include: named pipes, debug
-events, sockets, and timers. It's unlikely we'll want to optimize debug events
-or sockets (or any of the other, rather rare, objects), but it is possible
-we'll want to optimize named pipes or timers.
-
-There were two sort of complications when working out the above. The first one
-was events. The trouble is that (1) the server actually creates some events by
-itself and (2) the server sometimes manipulates events passed by the
-client. Resolving the first case was easy enough, and merely entailed creating
-eventfd descriptors for the events the same way as for processes and threads
-(note that we don't really lose anything this way; the events include
-"LowMemoryCondition" and the event that signals system processes to shut
-down). For the second case I basically had to hook the server-side event
-functions to redirect to esync versions if the event was actually an esync
-primitive.
-
-The second complication was message queues. The difficulty here is that X11
-signals events by writing into a pipe (at least I think it's a pipe?), and so
-as a result wineserver has to poll on that descriptor. In theory we could just
-let wineserver do so and then signal us as appropriate, except that wineserver
-only polls on the pipe when the thread is waiting for events (otherwise we'd
-get e.g. keyboard input while the thread is doing something else, and spin
-forever trying to wake up a thread that doesn't care). The obvious solution is
-just to poll on that fd ourselves, and that's what I did—it's just that
-getting the fd from wineserver was kind of ugly, and the code for waiting was
-also kind of ugly basically because we have to wait on both X11's fd and the
-"normal" process/thread-style wineserver fd that we use to signal sent
-messages. The upshot about the whole thing was that races are basically
-impossible, since a thread can only wait on its own queue.
-
-System APCs already work, since the server will forcibly suspend a thread if
-it's not already waiting, and so we just need to check for EINTR from
-poll(). User APCs and alertable waits are implemented in a similar style to
-message queues (well, sort of): whenever someone executes an alertable wait,
-we add an additional eventfd to the list, which the server signals when an APC
-arrives. If that eventfd gets signaled, we hand it off to the server to take
-care of, and return STATUS_USER_APC.
-
-Originally I kept the volatile state of semaphores and mutexes inside a
-variable local to the handle, with the knowledge that this would break if
-someone tried to open the handle elsewhere or duplicate it. It did, and so now
-this state is stored inside shared memory. This is of the POSIX variety, is
-allocated by the server (but never mapped there) and lives under the path
-"/wine-esync".
-
-There are a couple things that this infrastructure can't handle, although
-surprisingly there aren't that many. In particular:
-* Implementing wait-all, i.e. WaitForMultipleObjects(..., TRUE, ...), is not
-  exactly possible the way we'd like it to be possible. In theory that
-  function should wait until it knows all objects are available, then grab
-  them all at once atomically. The server (like the kernel) can do this
-  because the server is single-threaded and can't race with itself. We can't
-  do this in ntdll, though. The approach I've taken I've laid out in great
-  detail in the relevant patch, but for a quick summary we poll on each object
-  until it's signaled (but don't grab it), check them all again, and if
-  they're all signaled we try to grab them all at once in a tight loop, and if
-  we fail on any of them we reset the count on whatever we shouldn't have
-  consumed. Such a blip would necessarily be very quick.
-* The whole patchset only works on Linux, where eventfd is available. However,
-  it should be possible to make it work on a Mac, since eventfd is just a
-  quicker, easier way to use pipes (i.e. instead of writing 1 to the fd you'd
-  write 1 byte; instead of reading a 64-bit value from the fd you'd read as
-  many bytes as you can carry, which is admittedly less than 2**64 but
-  can probably be something reasonable.) It's also possible, although I
-  haven't yet looked, to use some different kind of synchronization
-  primitives, but pipes would be easiest to tack onto this framework.
-* PulseEvent() can't work the way it's supposed to work. Fortunately it's rare
-  and deprecated. It's also explicitly mentioned on MSDN that a thread can
-  miss the notification for a kernel APC, so in a sense we're not necessarily
-  doing anything wrong.
-
-There are some things that are perfectly implementable but that I just haven't
-done yet:
-* Other synchronizable server primitives. It's unlikely we'll need any of
-  these, except perhaps named pipes (which would honestly be rather difficult)
-  and (maybe) timers.
-* Access masks. We'd need to store these inside ntdll, and validate them when
-  someone tries to execute esync operations.
-
-This patchset was inspired by Daniel Santos' "hybrid synchronization"
-patchset. My idea was to create a framework whereby even contended waits could
-be executed in userspace, eliminating a lot of the complexity that his
-synchronization primitives used. I do however owe some significant gratitude
-toward him for setting me on the right path.
-
-I've tried to maximize code separation, both to make any potential rebases
-easier and to ensure that esync is only active when configured. All code in
-existing source files is guarded with "if (do_esync())", and generally that
-condition is followed by "return esync_version_of_this_method(...);", where
-the latter lives in esync.c and is declared in esync.h. I've also tried to
-make the patchset very clear and readable—to write it as if I were going to
-submit it upstream. (Some intermediate patches do break things, which Wine is
-generally against, but I think it's for the better in this case.) I have cut
-some corners, though; there is some error checking missing, or implicit
-assumptions that the program is behaving correctly.
-
-I've tried to be careful about races. There are a lot of comments whose
-purpose are basically to assure me that races are impossible. In most cases we
-don't have to worry about races since all of the low-level synchronization is
-done by the kernel.
-
-Anyway, yeah, this is esync. Use it if you like.
-
---Zebediah Figura
diff --git a/configure b/configure
index 3435c61..63ec1c8 100755
--- a/configure
+++ b/configure
@@ -7762,6 +7762,12 @@ if test "x$ac_cv_header_linux_major_h" = xyes
 then :
   printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h
 
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/ntsync.h" "ac_cv_header_linux_ntsync_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_ntsync_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h
+
 fi
 ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default"
 if test "x$ac_cv_header_linux_param_h" = xyes
diff --git a/configure.ac b/configure.ac
index 9f92048..da9db40 100644
--- a/configure.ac
+++ b/configure.ac
@@ -389,6 +389,7 @@ AC_CHECK_HEADERS(\
 	linux/input.h \
 	linux/ioctl.h \
 	linux/major.h \
+	linux/ntsync.h \
 	linux/param.h \
 	linux/seccomp.h \
 	linux/serial.h \
@@ -418,7 +419,6 @@ AC_CHECK_HEADERS(\
 	sys/cdio.h \
 	sys/epoll.h \
 	sys/event.h \
-	sys/eventfd.h \
 	sys/extattr.h \
 	sys/filio.h \
 	sys/ipc.h \
@@ -2090,7 +2090,6 @@ AC_CHECK_FUNCS(\
 	port_create \
 	posix_fadvise \
 	posix_fallocate \
-	ppoll \
 	prctl \
 	sched_getcpu \
 	sched_yield \
@@ -2112,12 +2111,6 @@ AC_SEARCH_LIBS(clock_gettime, rt,
                 test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")])
 LIBS=$ac_save_LIBS
 
-ac_save_LIBS=$LIBS
-AC_SEARCH_LIBS(shm_open, rt,
-               [AC_DEFINE(HAVE_SHM_OPEN, 1, [Define to 1 if you have the `shm_open' function.])
-                test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")])
-LIBS=$ac_save_LIBS
-
 AC_CACHE_CHECK([for sched_setaffinity],wine_cv_have_sched_setaffinity,
                 AC_LINK_IFELSE([AC_LANG_PROGRAM(
 [[#include <sched.h>]], [[sched_setaffinity(0, 0, 0);]])],[wine_cv_have_sched_setaffinity=yes],[wine_cv_have_sched_setaffinity=no]))
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index 5826cab..7a65714 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -58,7 +58,6 @@ static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK);
 
 static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG_PTR, SIZE_T *, ULONG, ULONG);
 static NTSTATUS (WINAPI *pNtFreeVirtualMemory)(HANDLE, PVOID *, SIZE_T *, ULONG);
-static NTSTATUS (WINAPI *pNtQuerySystemTime)(LARGE_INTEGER *);
 static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE, BOOLEAN, const LARGE_INTEGER *);
 static NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
 static PSLIST_ENTRY (__fastcall *pRtlInterlockedPushListSList)(PSLIST_HEADER list, PSLIST_ENTRY first,
@@ -229,23 +228,8 @@ static void test_temporary_objects(void)
     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %lu\n", GetLastError());
 }
 
-static HANDLE mutex, mutex2, mutices[2];
-
-static DWORD WINAPI mutex_thread( void *param )
-{
-    DWORD expect = (DWORD)(DWORD_PTR)param;
-    DWORD ret;
-
-    ret = WaitForSingleObject( mutex, 0 );
-    ok(ret == expect, "expected %lu, got %lu\n", expect, ret);
-
-    if (!ret) ReleaseMutex( mutex );
-    return 0;
-}
-
 static void test_mutex(void)
 {
-    HANDLE thread;
     DWORD wait_ret;
     BOOL ret;
     HANDLE hCreated;
@@ -283,8 +267,7 @@ static void test_mutex(void)
     SetLastError(0xdeadbeef);
     hOpened = OpenMutexA(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex");
     ok(hOpened != NULL, "OpenMutex failed with error %ld\n", GetLastError());
-    wait_ret = WaitForSingleObject(hOpened, 0);
-todo_wine_if(getenv("WINEESYNC"))   /* XFAIL: validation is not implemented */
+    wait_ret = WaitForSingleObject(hOpened, INFINITE);
     ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n");
     CloseHandle(hOpened);
 
@@ -315,7 +298,6 @@ todo_wine_if(getenv("WINEESYNC"))   /* XFAIL: validation is not implemented */
 
     SetLastError(0xdeadbeef);
     ret = ReleaseMutex(hCreated);
-todo_wine_if(getenv("WINEESYNC"))   /* XFAIL: due to the above */
     ok(!ret && (GetLastError() == ERROR_NOT_OWNER),
         "ReleaseMutex should have failed with ERROR_NOT_OWNER instead of %ld\n", GetLastError());
 
@@ -354,85 +336,6 @@ todo_wine_if(getenv("WINEESYNC"))   /* XFAIL: due to the above */
     CloseHandle(hOpened);
 
     CloseHandle(hCreated);
-
-    mutex = CreateMutexA( NULL, FALSE, NULL );
-    ok(!!mutex, "got error %lu\n", GetLastError());
-
-    ret = ReleaseMutex( mutex );
-    ok(!ret, "got %d\n", ret);
-    ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError());
-
-    for (i = 0; i < 100; i++)
-    {
-        ret = WaitForSingleObject( mutex, 0 );
-        ok(ret == 0, "got %u\n", ret);
-    }
-
-    for (i = 0; i < 100; i++)
-    {
-        ret = ReleaseMutex( mutex );
-        ok(ret, "got error %lu\n", GetLastError());
-    }
-
-    ret = ReleaseMutex( mutex );
-    ok(!ret, "got %d\n", ret);
-    ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError());
-
-    thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL );
-    ret = WaitForSingleObject( thread, 2000 );
-    ok(ret == 0, "wait failed: %u\n", ret);
-
-    WaitForSingleObject( mutex, 0 );
-
-    thread = CreateThread( NULL, 0, mutex_thread, (void *)WAIT_TIMEOUT, 0, NULL );
-    ret = WaitForSingleObject( thread, 2000 );
-    ok(ret == 0, "wait failed: %u\n", ret);
-
-    ret = ReleaseMutex( mutex );
-        ok(ret, "got error %lu\n", GetLastError());
-
-    thread = CreateThread( NULL, 0, mutex_thread, (void *)0, 0, NULL );
-    ret = WaitForSingleObject( thread, 2000 );
-    ok(ret == 0, "wait failed: %u\n", ret);
-
-    mutex2 = CreateMutexA( NULL, TRUE, NULL );
-    ok(!!mutex2, "got error %lu\n", GetLastError());
-
-    ret = ReleaseMutex( mutex2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ReleaseMutex( mutex2 );
-    ok(!ret, "got %d\n", ret);
-    ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError());
-
-    mutices[0] = mutex;
-    mutices[1] = mutex2;
-
-    ret = WaitForMultipleObjects( 2, mutices, FALSE, 0 );
-    ok(ret == 0, "got %u\n", ret);
-
-    ret = ReleaseMutex( mutex );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ReleaseMutex( mutex2 );
-    ok(!ret, "got %d\n", ret);
-    ok(GetLastError() == ERROR_NOT_OWNER, "got error %lu\n", GetLastError());
-
-    ret = WaitForMultipleObjects( 2, mutices, TRUE, 0 );
-    ok(ret == 0, "got %u\n", ret);
-
-    ret = ReleaseMutex( mutex );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ReleaseMutex( mutex2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = CloseHandle( mutex );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = CloseHandle( mutex2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
 }
 
 static void test_slist(void)
@@ -608,13 +511,12 @@ static void test_slist(void)
 
 static void test_event(void)
 {
-    HANDLE handle, handle2, handles[2];
+    HANDLE handle, handle2;
     SECURITY_ATTRIBUTES sa;
     SECURITY_DESCRIPTOR sd;
     ACL acl;
     DWORD ret;
     BOOL val;
-    int i;
 
     /* no sd */
     handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event");
@@ -718,130 +620,11 @@ static void test_event(void)
     ok( ret, "QueryMemoryResourceNotification failed err %lu\n", GetLastError() );
     ok( val == FALSE || val == TRUE, "wrong value %u\n", val );
     CloseHandle( handle );
-
-    handle = CreateEventA( NULL, TRUE, FALSE, NULL );
-    ok(!!handle, "got error %lu\n", GetLastError());
-
-    ret = WaitForSingleObject( handle, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ret = SetEvent( handle );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = SetEvent( handle );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    for (i = 0; i < 100; i++)
-    {
-        ret = WaitForSingleObject( handle, 0 );
-        ok(ret == 0, "got %lu\n", ret);
-    }
-
-    ret = ResetEvent( handle );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ResetEvent( handle );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = WaitForSingleObject( handle, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    handle2 = CreateEventA( NULL, FALSE, TRUE, NULL );
-    ok(!!handle2, "got error %lu\n", GetLastError());
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ret = SetEvent( handle2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = SetEvent( handle2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ResetEvent( handle2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = ResetEvent( handle2 );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    handles[0] = handle;
-    handles[1] = handle2;
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    SetEvent( handle );
-    SetEvent( handle2 );
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ResetEvent( handle );
-    SetEvent( handle2 );
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 1, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    SetEvent( handle );
-    SetEvent( handle2 );
-
-    ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    SetEvent( handle2 );
-    ResetEvent( handle );
-
-    ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    handles[0] = handle2;
-    handles[1] = handle;
-    SetEvent( handle );
-    SetEvent( handle2 );
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 1, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 1, "got %lu\n", ret);
-
-    ret = CloseHandle( handle );
-    ok(ret, "got error %lu\n", GetLastError());
-
-    ret = CloseHandle( handle2 );
-    ok(ret, "got error %lu\n", GetLastError());
 }
 
 static void test_semaphore(void)
 {
-    HANDLE handle, handle2, handles[2];
-    DWORD ret;
-    LONG prev;
-    int i;
+    HANDLE handle, handle2;
 
     /* test case sensitivity */
 
@@ -883,99 +666,6 @@ static void test_semaphore(void)
     ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %lu\n", GetLastError());
 
     CloseHandle( handle );
-
-    handle = CreateSemaphoreA( NULL, 0, 5, NULL );
-    ok(!!handle, "CreateSemaphore failed: %lu\n", GetLastError());
-
-    ret = WaitForSingleObject( handle, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ret = ReleaseSemaphore( handle, 1, &prev );
-    ok(ret, "got error %lu\n", GetLastError());
-    ok(prev == 0, "got prev %ld\n", prev);
-
-    ret = ReleaseSemaphore( handle, 1, &prev );
-    ok(ret, "got error %lu\n", GetLastError());
-    ok(prev == 1, "got prev %ld\n", prev);
-
-    ret = ReleaseSemaphore( handle, 5, &prev );
-    ok(!ret, "got %ld\n", ret);
-    ok(GetLastError() == ERROR_TOO_MANY_POSTS, "got error %lu\n", GetLastError());
-    ok(prev == 1, "got prev %ld\n", prev);
-
-    ret = ReleaseSemaphore( handle, 2, &prev );
-    ok(ret, "got error %lu\n", GetLastError());
-    ok(prev == 2, "got prev %ld\n", prev);
-
-    ret = ReleaseSemaphore( handle, 1, &prev );
-    ok(ret, "got error %lu\n", GetLastError());
-    ok(prev == 4, "got prev %ld\n", prev);
-
-    for (i = 0; i < 5; i++)
-    {
-        ret = WaitForSingleObject( handle, 0 );
-        ok(ret == 0, "got %lu\n", ret);
-    }
-
-    ret = WaitForSingleObject( handle, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    handle2 = CreateSemaphoreA( NULL, 3, 5, NULL );
-    ok(!!handle2, "CreateSemaphore failed: %lu\n", GetLastError());
-
-    ret = ReleaseSemaphore( handle2, 1, &prev );
-    ok(ret, "got error %lu\n", GetLastError());
-    ok(prev == 3, "got prev %ld\n", prev);
-
-    for (i = 0; i < 4; i++)
-    {
-        ret = WaitForSingleObject( handle2, 0 );
-        ok(ret == 0, "got %lu\n", ret);
-    }
-
-    ret = WaitForSingleObject( handle2, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    handles[0] = handle;
-    handles[1] = handle2;
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ReleaseSemaphore( handle, 1, NULL );
-    ReleaseSemaphore( handle2, 1, NULL );
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == 1, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ReleaseSemaphore( handle, 1, NULL );
-    ReleaseSemaphore( handle2, 1, NULL );
-
-    ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ReleaseSemaphore( handle, 1, NULL );
-
-    ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    ret = WaitForSingleObject( handle, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = CloseHandle( handle );
-    ok(ret, "got error %lu\n", ret);
-
-    ret = CloseHandle( handle2 );
-    ok(ret, "got error %lu\n", ret);
 }
 
 static void test_waitable_timer(void)
@@ -1530,15 +1220,11 @@ static HANDLE modify_handle(HANDLE handle, DWORD modify)
     return ULongToHandle(tmp);
 }
 
-#define TIMEOUT_INFINITE (((LONGLONG)0x7fffffff) << 32 | 0xffffffff)
-
 static void test_WaitForSingleObject(void)
 {
     HANDLE signaled, nonsignaled, invalid;
-    LARGE_INTEGER ntnow, ntthen;
     LARGE_INTEGER timeout;
     NTSTATUS status;
-    DWORD now, then;
     DWORD ret;
 
     signaled = CreateEventW(NULL, TRUE, TRUE, NULL);
@@ -1623,68 +1309,6 @@ static void test_WaitForSingleObject(void)
     status = pNtWaitForSingleObject(GetCurrentThread(), FALSE, &timeout);
     ok(status == STATUS_TIMEOUT, "expected STATUS_TIMEOUT, got %08lx\n", status);
 
-    ret = WaitForSingleObject( signaled, 0 );
-    ok(ret == 0, "got %lu\n", ret);
-
-    ret = WaitForSingleObject( nonsignaled, 0 );
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-
-    /* test that a timed wait actually does wait */
-    now = GetTickCount();
-    ret = WaitForSingleObject( nonsignaled, 100 );
-    then = GetTickCount();
-    ok(ret == WAIT_TIMEOUT, "got %lu\n", ret);
-    ok(abs((then - now) - 100) < 5, "got %lu ms\n", then - now);
-
-    now = GetTickCount();
-    ret = WaitForSingleObject( signaled, 100 );
-    then = GetTickCount();
-    ok(ret == 0, "got %lu\n", ret);
-    ok(abs(then - now) < 5, "got %lu ms\n", then - now);
-
-    ret = WaitForSingleObject( signaled, INFINITE );
-    ok(ret == 0, "got %lu\n", ret);
-
-    /* test NT timeouts */
-    pNtQuerySystemTime( &ntnow );
-    timeout.QuadPart = ntnow.QuadPart + 100 * 10000;
-    status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout );
-    pNtQuerySystemTime( &ntthen );
-    ok(status == STATUS_TIMEOUT, "got %#lx\n", status);
-    ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n",
-        wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100));
-
-    pNtQuerySystemTime( &ntnow );
-    timeout.QuadPart = -100 * 10000;
-    status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout );
-    pNtQuerySystemTime( &ntthen );
-    ok(status == STATUS_TIMEOUT, "got %#lx\n", status);
-    ok(abs(((ntthen.QuadPart - ntnow.QuadPart) / 10000) - 100) < 5, "got %s ns\n",
-        wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100));
-
-    status = pNtWaitForSingleObject( signaled, FALSE, NULL );
-    ok(status == 0, "got %#lx\n", status);
-
-    timeout.QuadPart = TIMEOUT_INFINITE;
-    status = pNtWaitForSingleObject( signaled, FALSE, &timeout );
-    ok(status == 0, "got %#lx\n", status);
-
-    pNtQuerySystemTime( &ntnow );
-    timeout.QuadPart = ntnow.QuadPart;
-    status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout );
-    pNtQuerySystemTime( &ntthen );
-    ok(status == STATUS_TIMEOUT, "got %#lx\n", status);
-    ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n",
-        wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100));
-
-    pNtQuerySystemTime( &ntnow );
-    timeout.QuadPart = ntnow.QuadPart - 100 * 10000;
-    status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout );
-    pNtQuerySystemTime( &ntthen );
-    ok(status == STATUS_TIMEOUT, "got %#lx\n", status);
-    ok(abs((ntthen.QuadPart - ntnow.QuadPart) / 10000) < 5, "got %s ns\n",
-        wine_dbgstr_longlong((ntthen.QuadPart - ntnow.QuadPart) * 100));
-
     CloseHandle(signaled);
     CloseHandle(nonsignaled);
 }
@@ -3278,84 +2902,6 @@ static void test_QueueUserAPC(void)
     ok(apc_count == 1, "APC count %u\n", apc_count);
 }
 
-static int zigzag_state, zigzag_count[2], zigzag_stop;
-
-static DWORD CALLBACK zigzag_event0(void *arg)
-{
-    HANDLE *events = arg;
-
-    while (!zigzag_stop)
-    {
-        WaitForSingleObject(events[0], INFINITE);
-        ResetEvent(events[0]);
-        ok(zigzag_state == 0, "got wrong state %d\n", zigzag_state);
-        zigzag_state++;
-        SetEvent(events[1]);
-        zigzag_count[0]++;
-    }
-    trace("thread 0 got done\n");
-    return 0;
-}
-
-static DWORD CALLBACK zigzag_event1(void *arg)
-{
-    HANDLE *events = arg;
-
-    while (!zigzag_stop)
-    {
-        WaitForSingleObject(events[1], INFINITE);
-        ResetEvent(events[1]);
-        ok(zigzag_state == 1, "got wrong state %d\n", zigzag_state);
-        zigzag_state--;
-        SetEvent(events[0]);
-        zigzag_count[1]++;
-    }
-    trace("thread 1 got done\n");
-    return 0;
-}
-
-static void test_zigzag_event(void)
-{
-    /* The basic idea is to test SetEvent/Wait back and forth between two
-     * threads. Each thread clears their own event, sets some common data,
-     * signals the other's, then waits on their own. We make sure the common
-     * data is always in the right state. We also print performance data. */
-
-    HANDLE threads[2], events[2];
-    BOOL ret;
-
-    events[0] = CreateEventA(NULL, FALSE, FALSE, NULL);
-    events[1] = CreateEventA(NULL, FALSE, FALSE, NULL);
-
-    threads[0] = CreateThread(NULL, 0, zigzag_event0, events, 0, NULL);
-    threads[1] = CreateThread(NULL, 0, zigzag_event1, events, 0, NULL);
-
-    zigzag_state = 0;
-    zigzag_count[0] = zigzag_count[1] = 0;
-    zigzag_stop = 0;
-
-    trace("starting zigzag test (events)\n");
-    SetEvent(events[0]);
-    Sleep(2000);
-    zigzag_stop = 1;
-    ret = WaitForMultipleObjects(2, threads, FALSE, INFINITE);
-    trace("%d\n", ret);
-    ok(ret == 0 || ret == 1, "wait failed: %u\n", ret);
-
-    ok(zigzag_count[0] == zigzag_count[1] || zigzag_count[0] == zigzag_count[1] + 1,
-        "count did not match: %d != %d\n", zigzag_count[0], zigzag_count[1]);
-
-    /* signal the other thread to finish, if it didn't already
-     * (in theory they both would at the same time, but there's a slight race on teardown if we get
-     * thread 1 SetEvent -> thread 0 ResetEvent -> thread 0 Wait -> thread 1 exits */
-    zigzag_state = 1-ret;
-    SetEvent(events[1-ret]);
-    ret = WaitForSingleObject(threads[1-ret], 1000);
-    ok(!ret, "wait failed: %u\n", ret);
-
-    trace("count: %d\n", zigzag_count[0]);
-}
-
 START_TEST(sync)
 {
     char **argv;
@@ -3382,7 +2928,6 @@ START_TEST(sync)
     pTryAcquireSRWLockShared = (void *)GetProcAddress(hdll, "TryAcquireSRWLockShared");
     pNtAllocateVirtualMemory = (void *)GetProcAddress(hntdll, "NtAllocateVirtualMemory");
     pNtFreeVirtualMemory = (void *)GetProcAddress(hntdll, "NtFreeVirtualMemory");
-    pNtQuerySystemTime = (void *)GetProcAddress(hntdll, "NtQuerySystemTime");
     pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject");
     pNtWaitForMultipleObjects = (void *)GetProcAddress(hntdll, "NtWaitForMultipleObjects");
     pRtlInterlockedPushListSList = (void *)GetProcAddress(hntdll, "RtlInterlockedPushListSList");
@@ -3427,6 +2972,5 @@ START_TEST(sync)
     test_srwlock_example();
     test_alertable_wait();
     test_apc_deadlock();
-    test_zigzag_event();
     test_crit_section();
 }
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index 189e59f..aad14d1 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -49,7 +49,6 @@ SOURCES = \
 	unix/cdrom.c \
 	unix/debug.c \
 	unix/env.c \
-	unix/esync.c \
 	unix/file.c \
 	unix/loader.c \
 	unix/loadorder.c \
diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c
deleted file mode 100644
index 86809b6..0000000
--- a/dlls/ntdll/unix/esync.c
+++ /dev/null
@@ -1,1325 +0,0 @@
-/*
- * eventfd-based synchronization objects
- *
- * Copyright (C) 2018 Zebediah Figura
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#if 0
-#pragma makedep unix
-#endif
-
-#include "config.h"
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#include <poll.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "ntstatus.h"
-#define WIN32_NO_STATUS
-#include "windef.h"
-#include "winternl.h"
-#include "wine/server.h"
-#include "wine/debug.h"
-
-#include "unix_private.h"
-#include "esync.h"
-
-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
-}
-
-struct esync
-{
-    LONG type;
-    int fd;
-    void *shm;
-};
-
-struct semaphore
-{
-    LONG max;
-    LONG count;
-};
-C_ASSERT(sizeof(struct semaphore) == 8);
-
-struct mutex
-{
-    LONG tid;
-    LONG count;    /* recursion count */
-};
-C_ASSERT(sizeof(struct mutex) == 8);
-
-struct event
-{
-    LONG signaled;
-    LONG locked;
-};
-C_ASSERT(sizeof(struct event) == 8);
-
-static char shm_name[29];
-static int shm_fd;
-static void **shm_addrs;
-static int shm_addrs_size;  /* length of the allocated shm_addrs array */
-static long pagesize;
-
-static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void *get_shm( unsigned int idx )
-{
-    int entry  = (idx * 8) / pagesize;
-    int offset = (idx * 8) % pagesize;
-    void *ret;
-
-    pthread_mutex_lock( &shm_addrs_mutex );
-
-    if (entry >= shm_addrs_size)
-    {
-        int new_size = max(shm_addrs_size * 2, entry + 1);
-
-        if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) )))
-            ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size);
-        memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) );
-        shm_addrs_size = new_size;
-    }
-
-    if (!shm_addrs[entry])
-    {
-        void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
-        if (addr == (void *)-1)
-            ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize);
-
-        TRACE("Mapping page %d at %p.\n", entry, addr);
-
-        if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 ))
-            munmap( addr, pagesize ); /* someone beat us to it */
-    }
-
-    ret = (void *)((unsigned long)shm_addrs[entry] + offset);
-
-    pthread_mutex_unlock( &shm_addrs_mutex );
-
-    return ret;
-}
-
-/* We'd like lookup to be fast. To that end, we use a static list indexed by handle.
- * This is copied and adapted from the fd cache code. */
-
-#define ESYNC_LIST_BLOCK_SIZE  (65536 / sizeof(struct esync))
-#define ESYNC_LIST_ENTRIES     256
-
-static struct esync *esync_list[ESYNC_LIST_ENTRIES];
-static struct esync esync_list_initial_block[ESYNC_LIST_BLOCK_SIZE];
-
-static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry )
-{
-    UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1;
-    *entry = idx / ESYNC_LIST_BLOCK_SIZE;
-    return idx % ESYNC_LIST_BLOCK_SIZE;
-}
-
-static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, void *shm )
-{
-    UINT_PTR entry, idx = handle_to_index( handle, &entry );
-
-    if (entry >= ESYNC_LIST_ENTRIES)
-    {
-        FIXME( "too many allocated handles, not caching %p\n", handle );
-        return FALSE;
-    }
-
-    if (!esync_list[entry])  /* do we need to allocate a new block of entries? */
-    {
-        if (!entry) esync_list[0] = esync_list_initial_block;
-        else
-        {
-            void *ptr = anon_mmap_alloc( ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync),
-                                         PROT_READ | PROT_WRITE );
-            if (ptr == MAP_FAILED) return FALSE;
-            esync_list[entry] = ptr;
-        }
-    }
-
-    if (!InterlockedCompareExchange( &esync_list[entry][idx].type, type, 0 ))
-    {
-        esync_list[entry][idx].fd = fd;
-        esync_list[entry][idx].shm = shm;
-    }
-    return &esync_list[entry][idx];
-}
-
-static struct esync *get_cached_object( HANDLE handle )
-{
-    UINT_PTR entry, idx = handle_to_index( handle, &entry );
-
-    if (entry >= ESYNC_LIST_ENTRIES || !esync_list[entry]) return NULL;
-    if (!esync_list[entry][idx].type) return NULL;
-
-    return &esync_list[entry][idx];
-}
-
-/* Gets an object. This is either a proper esync object (i.e. an event,
- * semaphore, etc. created using create_esync) or a generic synchronizable
- * server-side object which the server will signal (e.g. a process, thread,
- * message queue, etc.) */
-static NTSTATUS get_object( HANDLE handle, struct esync **obj )
-{
-    int ret = STATUS_SUCCESS;
-    enum esync_type type = 0;
-    unsigned int shm_idx = 0;
-    obj_handle_t fd_handle;
-    sigset_t sigset;
-    int fd = -1;
-
-    if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS;
-
-    if ((INT_PTR)handle < 0)
-    {
-        /* We can deal with pseudo-handles, but it's just easier this way */
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if (!handle)
-    {
-        /* Shadow of the Tomb Raider really likes passing in NULL handles to
-         * various functions. Concerning, but let's avoid a server call. */
-        return STATUS_INVALID_HANDLE;
-    }
-
-    /* We need to try grabbing it from the server. */
-    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
-    if (!(*obj = get_cached_object( handle )))
-    {
-        SERVER_START_REQ( get_esync_fd )
-        {
-            req->handle = wine_server_obj_handle( handle );
-            if (!(ret = wine_server_call( req )))
-            {
-                type = reply->type;
-                shm_idx = reply->shm_idx;
-                fd = receive_fd( &fd_handle );
-                assert( wine_server_ptr_handle(fd_handle) == handle );
-            }
-        }
-        SERVER_END_REQ;
-    }
-    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
-
-    if (*obj)
-    {
-        /* We managed to grab it while in the CS; return it. */
-        return STATUS_SUCCESS;
-    }
-
-    if (ret)
-    {
-        WARN("Failed to retrieve fd for handle %p, status %#x.\n", handle, ret);
-        *obj = NULL;
-        return ret;
-    }
-
-    TRACE("Got fd %d for handle %p.\n", fd, handle);
-
-    *obj = add_to_list( handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
-    return ret;
-}
-
-NTSTATUS esync_close( HANDLE handle )
-{
-    UINT_PTR entry, idx = handle_to_index( handle, &entry );
-
-    TRACE("%p.\n", handle);
-
-    if (entry < ESYNC_LIST_ENTRIES && esync_list[entry])
-    {
-        if (InterlockedExchange(&esync_list[entry][idx].type, 0))
-        {
-            close( esync_list[entry][idx].fd );
-            return STATUS_SUCCESS;
-        }
-    }
-
-    return STATUS_INVALID_HANDLE;
-}
-
-static NTSTATUS create_esync( enum esync_type type, HANDLE *handle, ACCESS_MASK access,
-                              const OBJECT_ATTRIBUTES *attr, int initval, int max )
-{
-    NTSTATUS ret;
-    data_size_t len;
-    struct object_attributes *objattr;
-    obj_handle_t fd_handle;
-    unsigned int shm_idx;
-    sigset_t sigset;
-    int fd;
-
-    if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
-
-    /* We have to synchronize on the fd cache CS so that our calls to
-     * receive_fd don't race with theirs. */
-    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
-    SERVER_START_REQ( create_esync )
-    {
-        req->access  = access;
-        req->initval = initval;
-        req->type    = type;
-        req->max     = max;
-        wine_server_add_data( req, objattr, len );
-        ret = wine_server_call( req );
-        if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
-        {
-            *handle = wine_server_ptr_handle( reply->handle );
-            type = reply->type;
-            shm_idx = reply->shm_idx;
-            fd = receive_fd( &fd_handle );
-            assert( wine_server_ptr_handle(fd_handle) == *handle );
-        }
-    }
-    SERVER_END_REQ;
-    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
-
-    if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
-    {
-        add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
-        TRACE("-> handle %p, fd %d.\n", *handle, fd);
-    }
-
-    free( objattr );
-    return ret;
-}
-
-static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
-    ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
-{
-    NTSTATUS ret;
-    obj_handle_t fd_handle;
-    unsigned int shm_idx;
-    sigset_t sigset;
-    int fd;
-
-    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
-    SERVER_START_REQ( open_esync )
-    {
-        req->access     = access;
-        req->attributes = attr->Attributes;
-        req->rootdir    = wine_server_obj_handle( attr->RootDirectory );
-        req->type       = type;
-        if (attr->ObjectName)
-            wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
-        if (!(ret = wine_server_call( req )))
-        {
-            *handle = wine_server_ptr_handle( reply->handle );
-            type = reply->type;
-            shm_idx = reply->shm_idx;
-            fd = receive_fd( &fd_handle );
-            assert( wine_server_ptr_handle(fd_handle) == *handle );
-        }
-    }
-    SERVER_END_REQ;
-    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
-
-    if (!ret)
-    {
-        add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
-
-        TRACE("-> handle %p, fd %d.\n", *handle, fd);
-    }
-    return ret;
-}
-
-extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, int initial, int max)
-{
-    TRACE("name %s, initial %d, max %d.\n",
-        attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
-
-    return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max );
-}
-
-NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr )
-{
-    TRACE("name %s.\n", debugstr_us(attr->ObjectName));
-
-    return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
-}
-
-NTSTATUS esync_release_semaphore( HANDLE handle, unsigned int count, ULONG *prev )
-{
-    struct esync *obj;
-    struct semaphore *semaphore;
-    uint64_t count64 = count;
-    ULONG current;
-    NTSTATUS ret;
-
-    TRACE("%p, %d, %p.\n", handle, count, prev);
-
-    if ((ret = get_object( handle, &obj))) return ret;
-    semaphore = obj->shm;
-
-    do
-    {
-        current = semaphore->count;
-
-        if (count + current > semaphore->max)
-            return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
-    } while (InterlockedCompareExchange( &semaphore->count, count + current, current ) != current);
-
-    if (prev) *prev = current;
-
-    /* We don't have to worry about a race between increasing the count and
-     * 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)
-        return errno_to_status( errno );
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len )
-{
-    struct esync *obj;
-    struct semaphore *semaphore;
-    SEMAPHORE_BASIC_INFORMATION *out = info;
-    NTSTATUS ret;
-
-    TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-    semaphore = obj->shm;
-
-    out->CurrentCount = semaphore->count;
-    out->MaximumCount = semaphore->max;
-    if (ret_len) *ret_len = sizeof(*out);
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial )
-{
-    enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT);
-
-    TRACE("name %s, %s-reset, initial %d.\n",
-        attr ? debugstr_us(attr->ObjectName) : "<no name>",
-        event_type == NotificationEvent ? "manual" : "auto", initial);
-
-    return create_esync( type, handle, access, attr, initial, 0 );
-}
-
-NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr )
-{
-    TRACE("name %s.\n", debugstr_us(attr->ObjectName));
-
-    return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
-}
-
-static inline void small_pause(void)
-{
-#ifdef __i386__
-    __asm__ __volatile__( "rep;nop" : : : "memory" );
-#else
-    __asm__ __volatile__( "" : : : "memory" );
-#endif
-}
-
-/* Manual-reset events are actually racier than other objects in terms of shm
- * state. With other objects, races don't matter, because we only treat the shm
- * state as a hint that lets us skip poll()—we still have to read(). But with
- * manual-reset events we don't, which means that the shm state can be out of
- * sync with the actual state.
- *
- * In general we shouldn't have to worry about races between modifying the
- * event and waiting on it. If the state changes while we're waiting, it's
- * equally plausible that we caught it before or after the state changed.
- * However, we can have races between SetEvent() and ResetEvent(), so that the
- * event has inconsistent internal state.
- *
- * To solve this we have to use the other field to lock the event. Currently
- * this is implemented as a spinlock, but I'm not sure if a futex might be
- * better. I'm also not sure if it's possible to obviate locking by arranging
- * writes and reads in a certain way.
- *
- * Note that we don't have to worry about locking in esync_wait_objects().
- * There's only two general patterns:
- *
- * WaitFor()    SetEvent()
- * -------------------------
- * read()
- * signaled = 0
- *              signaled = 1
- *              write()
- * -------------------------
- * read()
- *              signaled = 1
- * signaled = 0
- *              <no write(), because it was already signaled>
- * -------------------------
- *
- * That is, if SetEvent() tries to signal the event before WaitFor() resets its
- * signaled state, it won't bother trying to write(), and then the signaled
- * state will be reset, so the result is a consistent non-signaled event.
- * There's several variations to this pattern but all of them are protected in
- * the same way. Note however this is why we have to use interlocked_xchg()
- * event inside of the lock.
- */
-
-/* Removing this spinlock is harder than it looks. esync_wait_objects() can
- * deal with inconsistent state well enough, and a race between SetEvent() and
- * ResetEvent() gives us license to yield either result as long as we act
- * consistently, but that's not enough. Notably, esync_wait_objects() should
- * probably act like a fence, so that the second half of esync_set_event() does
- * not seep past a subsequent reset. That's one problem, but no guarantee there
- * aren't others. */
-
-NTSTATUS esync_set_event( HANDLE handle )
-{
-    static const uint64_t value = 1;
-    struct esync *obj;
-    struct event *event;
-    NTSTATUS ret;
-
-    TRACE("%p.\n", handle);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-    event = obj->shm;
-
-    if (obj->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Acquire the spinlock. */
-        while (InterlockedCompareExchange( &event->locked, 1, 0 ))
-            small_pause();
-    }
-
-    /* For manual-reset events, as long as we're in a lock, we can take the
-     * optimization of only calling write() if the event wasn't already
-     * signaled.
-     *
-     * For auto-reset events, esync_wait_objects() must grab the kernel object.
-     * Thus if we got into a race so that the shm state is signaled but the
-     * eventfd is unsignaled (i.e. reset shm, set shm, set fd, reset fd), we
-     * *must* signal the fd now, or any waiting threads will never wake up. */
-
-    if (!InterlockedExchange( &event->signaled, 1 ) || obj->type == ESYNC_AUTO_EVENT)
-    {
-        if (write( obj->fd, &value, sizeof(value) ) == -1)
-            ERR("write: %s\n", strerror(errno));
-    }
-
-    if (obj->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Release the spinlock. */
-        event->locked = 0;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_reset_event( HANDLE handle )
-{
-    uint64_t value;
-    struct esync *obj;
-    struct event *event;
-    NTSTATUS ret;
-
-    TRACE("%p.\n", handle);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-    event = obj->shm;
-
-    if (obj->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Acquire the spinlock. */
-        while (InterlockedCompareExchange( &event->locked, 1, 0 ))
-            small_pause();
-    }
-
-    /* For manual-reset events, as long as we're in a lock, we can take the
-     * optimization of only calling read() if the event was already signaled.
-     *
-     * For auto-reset events, we have no guarantee that the previous "signaled"
-     * state is actually correct. We need to leave both states unsignaled after
-     * leaving this function, so we always have to read(). */
-    if (InterlockedExchange( &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));
-        }
-    }
-
-    if (obj->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Release the spinlock. */
-        event->locked = 0;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_pulse_event( HANDLE handle )
-{
-    uint64_t value = 1;
-    struct esync *obj;
-    NTSTATUS ret;
-
-    TRACE("%p.\n", handle);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-
-    /* 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)
-        return errno_to_status( errno );
-
-    /* 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) );
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len )
-{
-    struct esync *obj;
-    EVENT_BASIC_INFORMATION *out = info;
-    struct pollfd fd;
-    NTSTATUS ret;
-
-    TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-
-    fd.fd = obj->fd;
-    fd.events = POLLIN;
-    out->EventState = poll( &fd, 1, 0 );
-    out->EventType = (obj->type == ESYNC_AUTO_EVENT ? SynchronizationEvent : NotificationEvent);
-    if (ret_len) *ret_len = sizeof(*out);
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
-{
-    TRACE("name %s, initial %d.\n",
-        attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
-
-    return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
-}
-
-NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr )
-{
-    TRACE("name %s.\n", debugstr_us(attr->ObjectName));
-
-    return open_esync( ESYNC_MUTEX, handle, access, attr );
-}
-
-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);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-    mutex = obj->shm;
-
-    /* This is thread-safe, because the only thread that can change the tid to
-     * or from our tid is ours. */
-    if (mutex->tid != GetCurrentThreadId()) return STATUS_MUTANT_NOT_OWNED;
-
-    if (prev) *prev = mutex->count;
-
-    mutex->count--;
-
-    if (!mutex->count)
-    {
-        /* This is also thread-safe, as long as signaling the file is the last
-         * thing we do. Other threads don't care about the tid if it isn't
-         * theirs. */
-        mutex->tid = 0;
-
-        if (write( obj->fd, &value, sizeof(value) ) == -1)
-            return errno_to_status( errno );
-    }
-
-    return STATUS_SUCCESS;
-}
-
-NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len )
-{
-    struct esync *obj;
-    struct mutex *mutex;
-    MUTANT_BASIC_INFORMATION *out = info;
-    NTSTATUS ret;
-
-    TRACE("handle %p, info %p, ret_len %p.\n", handle, info, ret_len);
-
-    if ((ret = get_object( handle, &obj ))) return ret;
-    mutex = obj->shm;
-
-    out->CurrentCount = 1 - mutex->count;
-    out->OwnedByCaller = (mutex->tid == GetCurrentThreadId());
-    out->AbandonedState = (mutex->tid == ~0);
-    if (ret_len) *ret_len = sizeof(*out);
-
-    return STATUS_SUCCESS;
-}
-
-#define TICKSPERSEC        10000000
-#define TICKSPERMSEC       10000
-
-static LONGLONG update_timeout( ULONGLONG end )
-{
-    LARGE_INTEGER now;
-    LONGLONG timeleft;
-
-    NtQuerySystemTime( &now );
-    timeleft = end - now.QuadPart;
-    if (timeleft < 0) timeleft = 0;
-    return timeleft;
-}
-
-static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end )
-{
-    int ret;
-
-    do
-    {
-        if (end)
-        {
-            LONGLONG timeleft = update_timeout( *end );
-
-#ifdef HAVE_PPOLL
-            /* We use ppoll() if available since the time granularity is better. */
-            struct timespec tmo_p;
-            tmo_p.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC;
-            tmo_p.tv_nsec = (timeleft % TICKSPERSEC) * 100;
-            ret = ppoll( fds, nfds, &tmo_p, NULL );
-#else
-            ret = poll( fds, nfds, timeleft / TICKSPERMSEC );
-#endif
-        }
-        else
-            ret = poll( fds, nfds, -1 );
-
-    /* If we receive EINTR we were probably suspended (SIGUSR1), possibly for a
-     * system APC. The right thing to do is just try again. */
-    } while (ret < 0 && errno == EINTR);
-
-    return ret;
-}
-
-/* Return TRUE if abandoned. */
-static BOOL update_grabbed_object( struct esync *obj )
-{
-    BOOL ret = FALSE;
-
-    if (obj->type == ESYNC_MUTEX)
-    {
-        struct mutex *mutex = obj->shm;
-        /* We don't have to worry about a race between this and read(); the
-         * fact that we grabbed it means the count is now zero, so nobody else
-         * can (and the only thread that can release it is us). */
-        if (mutex->tid == ~0)
-            ret = TRUE;
-        mutex->tid = GetCurrentThreadId();
-        mutex->count++;
-    }
-    else if (obj->type == ESYNC_SEMAPHORE)
-    {
-        struct semaphore *semaphore = obj->shm;
-        /* We don't have to worry about a race between this and read(); the
-         * fact that we were able to grab it at all means the count is nonzero,
-         * and if someone else grabbed it then the count must have been >= 2,
-         * etc. */
-        InterlockedExchangeAdd( &semaphore->count, -1 );
-    }
-    else if (obj->type == ESYNC_AUTO_EVENT)
-    {
-        struct event *event = obj->shm;
-        /* We don't have to worry about a race between this and read(), since
-         * this is just a hint, and the real state is in the kernel object.
-         * This might already be 0, but that's okay! */
-        event->signaled = 0;
-    }
-
-    return ret;
-}
-
-/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we
- * need to delegate to server_select(). */
-static NTSTATUS __esync_wait_objects( unsigned int count, const HANDLE *handles, BOOLEAN wait_any,
-                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
-{
-    static const LARGE_INTEGER zero;
-
-    struct esync *objs[MAXIMUM_WAIT_OBJECTS];
-    struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1];
-    int has_esync = 0, has_server = 0;
-    BOOL msgwait = FALSE;
-    LONGLONG timeleft;
-    LARGE_INTEGER now;
-    DWORD pollcount;
-    ULONGLONG end;
-    int64_t value;
-    ssize_t size;
-    int i, j, ret;
-
-    /* Grab the APC fd if we don't already have it. */
-    if (alertable && ntdll_get_thread_data()->esync_apc_fd == -1)
-    {
-        obj_handle_t fd_handle;
-        sigset_t sigset;
-        int fd = -1;
-
-        server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
-        SERVER_START_REQ( get_esync_apc_fd )
-        {
-            if (!(ret = wine_server_call( req )))
-            {
-                fd = receive_fd( &fd_handle );
-                assert( fd_handle == GetCurrentThreadId() );
-            }
-        }
-        SERVER_END_REQ;
-        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
-
-        ntdll_get_thread_data()->esync_apc_fd = fd;
-    }
-
-    NtQuerySystemTime( &now );
-    if (timeout)
-    {
-        if (timeout->QuadPart == TIMEOUT_INFINITE)
-            timeout = NULL;
-        else if (timeout->QuadPart >= 0)
-            end = timeout->QuadPart;
-        else
-            end = now.QuadPart - timeout->QuadPart;
-    }
-
-    for (i = 0; i < count; i++)
-    {
-        ret = get_object( handles[i], &objs[i] );
-        if (ret == STATUS_SUCCESS)
-            has_esync = 1;
-        else if (ret == STATUS_NOT_IMPLEMENTED)
-            has_server = 1;
-        else
-            return ret;
-    }
-
-    if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE)
-        msgwait = TRUE;
-
-    if (has_esync && has_server)
-        FIXME("Can't wait on esync and server objects at the same time!\n");
-    else if (has_server)
-        return STATUS_NOT_IMPLEMENTED;
-
-    if (TRACE_ON(esync))
-    {
-        TRACE("Waiting for %s of %d handles:", wait_any ? "any" : "all", count);
-        for (i = 0; i < count; i++)
-            TRACE(" %p", handles[i]);
-
-        if (msgwait)
-            TRACE(" or driver events");
-        if (alertable)
-            TRACE(", alertable");
-
-        if (!timeout)
-            TRACE(", timeout = INFINITE.\n");
-        else
-        {
-            timeleft = update_timeout( end );
-            TRACE(", timeout = %ld.%07ld sec.\n",
-                (long) timeleft / TICKSPERSEC, (long) timeleft % TICKSPERSEC);
-        }
-    }
-
-    if (wait_any || count == 1)
-    {
-        /* Try to check objects now, so we can obviate poll() at least. */
-        for (i = 0; i < count; i++)
-        {
-            struct esync *obj = objs[i];
-
-            if (obj)
-            {
-                switch (obj->type)
-                {
-                case ESYNC_MUTEX:
-                {
-                    struct mutex *mutex = obj->shm;
-
-                    if (mutex->tid == GetCurrentThreadId())
-                    {
-                        TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                        mutex->count++;
-                        return i;
-                    }
-                    else if (!mutex->count)
-                    {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
-                        {
-                            if (mutex->tid == ~0)
-                            {
-                                TRACE("Woken up by abandoned mutex %p [%d].\n", handles[i], i);
-                                i += STATUS_ABANDONED_WAIT_0;
-                            }
-                            else
-                                TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                            mutex->tid = GetCurrentThreadId();
-                            mutex->count++;
-                            return i;
-                        }
-                    }
-                    break;
-                }
-                case ESYNC_SEMAPHORE:
-                {
-                    struct semaphore *semaphore = obj->shm;
-
-                    if (semaphore->count)
-                    {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
-                        {
-                            TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                            InterlockedDecrement( &semaphore->count );
-                            return i;
-                        }
-                    }
-                    break;
-                }
-                case ESYNC_AUTO_EVENT:
-                {
-                    struct event *event = obj->shm;
-
-                    if (event->signaled)
-                    {
-                        if ((size = read( obj->fd, &value, sizeof(value) )) == sizeof(value))
-                        {
-                            TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                            event->signaled = 0;
-                            return i;
-                        }
-                    }
-                    break;
-                }
-                case ESYNC_MANUAL_EVENT:
-                {
-                    struct event *event = obj->shm;
-
-                    if (event->signaled)
-                    {
-                        TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                        return i;
-                    }
-                    break;
-                }
-                case ESYNC_AUTO_SERVER:
-                case ESYNC_MANUAL_SERVER:
-                case ESYNC_QUEUE:
-                    /* We can't wait on any of these. Fortunately I don't think
-                     * they'll ever be uncontended anyway (at least, they won't be
-                     * performance-critical). */
-                    break;
-                }
-            }
-
-            fds[i].fd = obj ? obj->fd : -1;
-            fds[i].events = POLLIN;
-        }
-        if (alertable)
-        {
-            fds[i].fd = ntdll_get_thread_data()->esync_apc_fd;
-            fds[i].events = POLLIN;
-            i++;
-        }
-        pollcount = i;
-
-        while (1)
-        {
-            ret = do_poll( fds, pollcount, timeout ? &end : NULL );
-            if (ret > 0)
-            {
-                /* We must check this first! The server may set an event that
-                 * we're waiting on, but we need to return STATUS_USER_APC. */
-                if (alertable)
-                {
-                    if (fds[pollcount - 1].revents & POLLIN)
-                        goto userapc;
-                }
-
-                /* Find out which object triggered the wait. */
-                for (i = 0; i < count; i++)
-                {
-                    struct esync *obj = objs[i];
-
-                    if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL))
-                    {
-                        ERR("Polling on fd %d returned %#x.\n", fds[i].fd, fds[i].revents);
-                        return STATUS_INVALID_HANDLE;
-                    }
-
-                    if (obj)
-                    {
-                        if (obj->type == ESYNC_MANUAL_EVENT
-                                || obj->type == ESYNC_MANUAL_SERVER
-                                || obj->type == ESYNC_QUEUE)
-                        {
-                            /* Don't grab the object, just check if it's signaled. */
-                            if (fds[i].revents & POLLIN)
-                            {
-                                TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                                return i;
-                            }
-                        }
-                        else
-                        {
-                            if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value))
-                            {
-                                /* We found our object. */
-                                TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                                if (update_grabbed_object( obj ))
-                                    return STATUS_ABANDONED_WAIT_0 + i;
-                                return i;
-                            }
-                        }
-                    }
-                }
-
-                /* If we got here, someone else stole (or reset, etc.) whatever
-                 * we were waiting for. So keep waiting. */
-                NtQuerySystemTime( &now );
-            }
-            else
-                goto err;
-        }
-    }
-    else
-    {
-        /* Wait-all is a little trickier to implement correctly. Fortunately,
-         * it's not as common.
-         *
-         * The idea is basically just to wait in sequence on every object in the
-         * set. Then when we're done, try to grab them all in a tight loop. If
-         * that fails, release any resources we've grabbed (and yes, we can
-         * reliably do this—it's just mutexes and semaphores that we have to
-         * put back, and in both cases we just put back 1), and if any of that
-         * fails we start over.
-         *
-         * What makes this inherently bad is that we might temporarily grab a
-         * resource incorrectly. Hopefully it'll be quick (and hey, it won't
-         * block on wineserver) so nobody will notice. Besides, consider: if
-         * object A becomes signaled but someone grabs it before we can grab it
-         * and everything else, then they could just as well have grabbed it
-         * before it became signaled. Similarly if object A was signaled and we
-         * were blocking on object B, then B becomes available and someone grabs
-         * A before we can, then they might have grabbed A before B became
-         * signaled. In either case anyone who tries to wait on A or B will be
-         * waiting for an instant while we put things back. */
-
-        while (1)
-        {
-tryagain:
-            /* First step: try to poll on each object in sequence. */
-            fds[0].events = POLLIN;
-            pollcount = 1;
-            if (alertable)
-            {
-                /* We also need to wait on APCs. */
-                fds[1].fd = ntdll_get_thread_data()->esync_apc_fd;
-                fds[1].events = POLLIN;
-                pollcount++;
-            }
-            for (i = 0; i < count; i++)
-            {
-                struct esync *obj = objs[i];
-
-                fds[0].fd = obj ? obj->fd : -1;
-
-                if (obj && obj->type == ESYNC_MUTEX)
-                {
-                    /* It might be ours. */
-                    struct mutex *mutex = obj->shm;
-
-                    if (mutex->tid == GetCurrentThreadId())
-                        continue;
-                }
-
-                ret = do_poll( fds, pollcount, timeout ? &end : NULL );
-                if (ret <= 0)
-                    goto err;
-                else if (alertable && (fds[1].revents & POLLIN))
-                    goto userapc;
-
-                if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL))
-                {
-                    ERR("Polling on fd %d returned %#x.\n", fds[0].fd, fds[0].revents);
-                    return STATUS_INVALID_HANDLE;
-                }
-            }
-
-            /* If we got here and we haven't timed out, that means all of the
-             * 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].events = POLLIN;
-            }
-            /* There's no reason to check for APCs here. */
-            pollcount = i;
-
-            /* Poll everything to see if they're still signaled. */
-            ret = poll( fds, pollcount, 0 );
-            if (ret == pollcount)
-            {
-                BOOL abandoned = FALSE;
-
-                /* Quick, grab everything. */
-                for (i = 0; i < count; i++)
-                {
-                    struct esync *obj = objs[i];
-
-                    switch (obj->type)
-                    {
-                    case ESYNC_MUTEX:
-                    {
-                        struct mutex *mutex = obj->shm;
-                        if (mutex->tid == GetCurrentThreadId())
-                            break;
-                        /* otherwise fall through */
-                    }
-                    case ESYNC_SEMAPHORE:
-                    case ESYNC_AUTO_EVENT:
-                        if ((size = read( fds[i].fd, &value, sizeof(value) )) != sizeof(value))
-                        {
-                            /* We were too slow. Put everything back. */
-                            value = 1;
-                            for (j = i; j >= 0; j--)
-                            {
-                                if (write( obj->fd, &value, sizeof(value) ) == -1)
-                                    return errno_to_status( errno );
-                            }
-
-                            goto tryagain;  /* break out of two loops and a switch */
-                        }
-                        break;
-                    default:
-                        /* If a manual-reset event changed between there and
-                         * here, it's shouldn't be a problem. */
-                        break;
-                    }
-                }
-
-                /* If we got here, we successfully waited on every object. */
-                /* Make sure to let ourselves know that we grabbed the mutexes
-                 * and semaphores. */
-                for (i = 0; i < count; i++)
-                    abandoned |= update_grabbed_object( objs[i] );
-
-                if (abandoned)
-                {
-                    TRACE("Wait successful, but some object(s) were abandoned.\n");
-                    return STATUS_ABANDONED;
-                }
-                TRACE("Wait successful.\n");
-                return STATUS_SUCCESS;
-            }
-
-            /* If we got here, ppoll() returned less than all of our objects.
-             * So loop back to the beginning and try again. */
-        } /* while(1) */
-    } /* else (wait-all) */
-
-err:
-    /* We should only get here if poll() failed. */
-
-    if (ret == 0)
-    {
-        TRACE("Wait timed out.\n");
-        return STATUS_TIMEOUT;
-    }
-    else
-    {
-        ERR("ppoll failed: %s\n", strerror(errno));
-        return errno_to_status( errno );
-    }
-
-userapc:
-    TRACE("Woken up by user APC.\n");
-
-    /* We have to make a server call anyway to get the APC to execute, so just
-     * delegate down to server_select(). */
-    ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero );
-
-    /* This can happen if we received a system APC, and the APC fd was woken up
-     * before we got SIGUSR1. poll() doesn't return EINTR in that case. The
-     * right thing to do seems to be to return STATUS_USER_APC anyway. */
-    if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC;
-    return ret;
-}
-
-/* We need to let the server know when we are doing a message wait, and when we
- * are done with one, so that all of the code surrounding hung queues works.
- * We also need this for WaitForInputIdle(). */
-static void server_set_msgwait( int in_msgwait )
-{
-    SERVER_START_REQ( esync_msgwait )
-    {
-        req->in_msgwait = in_msgwait;
-        wine_server_call( req );
-    }
-    SERVER_END_REQ;
-}
-
-/* This is a very thin wrapper around the proper implementation above. The
- * purpose is to make sure the server knows when we are doing a message wait.
- * This is separated into a wrapper function since there are at least a dozen
- * exit paths from esync_wait_objects(). */
-NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
-                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
-{
-    BOOL msgwait = FALSE;
-    struct esync *obj;
-    NTSTATUS ret;
-
-    if (count && !get_object( handles[count - 1], &obj ) && obj->type == ESYNC_QUEUE)
-    {
-        msgwait = TRUE;
-        server_set_msgwait( 1 );
-    }
-
-    ret = __esync_wait_objects( count, handles, wait_any, alertable, timeout );
-
-    if (msgwait)
-        server_set_msgwait( 0 );
-
-    return ret;
-}
-
-NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable,
-    const LARGE_INTEGER *timeout )
-{
-    struct esync *obj;
-    NTSTATUS ret;
-
-    if ((ret = get_object( signal, &obj ))) return ret;
-
-    switch (obj->type)
-    {
-    case ESYNC_SEMAPHORE:
-        ret = esync_release_semaphore( signal, 1, NULL );
-        break;
-    case ESYNC_AUTO_EVENT:
-    case ESYNC_MANUAL_EVENT:
-        ret = esync_set_event( signal );
-        break;
-    case ESYNC_MUTEX:
-        ret = esync_release_mutex( signal, NULL );
-        break;
-    default:
-        return STATUS_OBJECT_TYPE_MISMATCH;
-    }
-    if (ret) return ret;
-
-    return esync_wait_objects( 1, &wait, TRUE, alertable, timeout );
-}
-
-void esync_init(void)
-{
-    struct stat st;
-
-    if (!do_esync())
-    {
-        /* make sure the server isn't running with WINEESYNC */
-        HANDLE handle;
-        NTSTATUS ret;
-
-        ret = create_esync( 0, &handle, 0, NULL, 0, 0 );
-        if (ret != STATUS_NOT_IMPLEMENTED)
-        {
-            ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n");
-            exit(1);
-        }
-
-        return;
-    }
-
-    if (stat( config_dir, &st ) == -1)
-        ERR("Cannot stat %s\n", config_dir);
-
-    if (st.st_ino != (unsigned long)st.st_ino)
-        sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino );
-    else
-        sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino );
-
-    if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1)
-    {
-        /* probably the server isn't running with WINEESYNC, tell the user and bail */
-        if (errno == ENOENT)
-            ERR("Failed to open esync shared memory file; make sure no stale wineserver instances are running without WINEESYNC.\n");
-        else
-            ERR("Failed to initialize shared memory: %s\n", strerror( errno ));
-        exit(1);
-    }
-
-    pagesize = sysconf( _SC_PAGESIZE );
-
-    shm_addrs = calloc( 128, sizeof(shm_addrs[0]) );
-    shm_addrs_size = 128;
-}
diff --git a/dlls/ntdll/unix/esync.h b/dlls/ntdll/unix/esync.h
deleted file mode 100644
index 9102cf9..0000000
--- a/dlls/ntdll/unix/esync.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * eventfd-based synchronization objects
- *
- * Copyright (C) 2018 Zebediah Figura
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-extern int do_esync(void);
-extern void esync_init(void);
-extern NTSTATUS esync_close( HANDLE handle );
-
-extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, int initial, int max);
-extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr );
-extern NTSTATUS esync_query_semaphore( HANDLE handle, void *info, ULONG *ret_len );
-extern NTSTATUS esync_release_semaphore( HANDLE handle, unsigned int count, ULONG *prev );
-
-extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial );
-extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr );
-extern NTSTATUS esync_pulse_event( HANDLE handle );
-extern NTSTATUS esync_query_event( HANDLE handle, void *info, ULONG *ret_len );
-extern NTSTATUS esync_reset_event( HANDLE handle );
-extern NTSTATUS esync_set_event( HANDLE handle );
-
-extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr, BOOLEAN initial );
-extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
-    const OBJECT_ATTRIBUTES *attr );
-extern NTSTATUS esync_query_mutex( HANDLE handle, void *info, ULONG *ret_len );
-extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev );
-
-extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
-                                    BOOLEAN alertable, const LARGE_INTEGER *timeout );
-extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable,
-    const LARGE_INTEGER *timeout );
-
-
-/* We have to synchronize on the fd cache mutex so that our calls to receive_fd
- * don't race with theirs. It looks weird, I know.
- *
- * If we weren't trying to avoid touching the code I'd rename the mutex to
- * "server_fd_mutex" or something similar. */
-extern pthread_mutex_t fd_cache_mutex;
-
-extern int receive_fd( obj_handle_t *handle );
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 082f82d..fc914a3 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -7666,7 +7666,7 @@ NTSTATUS WINAPI NtLockFile( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void
         }
         if (handle)
         {
-            NtWaitForSingleObject( handle, FALSE, NULL );
+            server_wait_for_object( handle, FALSE, NULL );
             NtClose( handle );
         }
         else  /* Unix lock conflict, sleep a bit and retry */
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index 77f95e4..0bade5b 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -90,7 +90,6 @@
 #include "winioctl.h"
 #include "winternl.h"
 #include "unix_private.h"
-#include "esync.h"
 #include "wine/list.h"
 #include "ntsyscalls.h"
 #include "wine/debug.h"
@@ -1844,7 +1843,6 @@ static void start_main_thread(void)
     signal_alloc_thread( teb );
     dbg_init();
     startup_info_size = server_init_process();
-    esync_init();
     virtual_map_user_shared_data();
     init_cpu_info();
     init_files();
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index 5339402..ebddb7a 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -920,7 +920,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
 
     /* wait for the new process info to be ready */
 
-    NtWaitForSingleObject( process_info, FALSE, NULL );
+    server_wait_for_object( process_info, FALSE, NULL );
     SERVER_START_REQ( get_new_process_info )
     {
         req->info = wine_server_obj_handle( process_info );
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c
index 7732338..5542d82 100644
--- a/dlls/ntdll/unix/server.c
+++ b/dlls/ntdll/unix/server.c
@@ -83,7 +83,6 @@
 #include "wine/server.h"
 #include "wine/debug.h"
 #include "unix_private.h"
-#include "esync.h"
 #include "ddk/wdm.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(server);
@@ -810,6 +809,21 @@ unsigned int server_wait( const union select_op *select_op, data_size_t size, UI
 }
 
 
+/* helper function to perform a server-side wait on an internal handle without
+ * using the fast synchronization path */
+unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout )
+{
+    union select_op select_op;
+    UINT flags = SELECT_INTERRUPTIBLE;
+
+    if (alertable) flags |= SELECT_ALERTABLE;
+
+    select_op.wait.op = SELECT_WAIT;
+    select_op.wait.handles[0] = wine_server_obj_handle( handle );
+    return server_wait( &select_op, offsetof( union select_op, wait.handles[1] ), flags, timeout );
+}
+
+
 /***********************************************************************
  *              NtContinue  (NTDLL.@)
  */
@@ -886,7 +900,7 @@ unsigned int server_queue_process_apc( HANDLE process, const union apc_call *cal
         }
         else
         {
-            NtWaitForSingleObject( handle, FALSE, NULL );
+            server_wait_for_object( handle, FALSE, NULL );
 
             SERVER_START_REQ( get_apc_result )
             {
@@ -953,7 +967,7 @@ void wine_server_send_fd( int fd )
  *
  * Receive a file descriptor passed from the server.
  */
-int receive_fd( obj_handle_t *handle )
+static int receive_fd( obj_handle_t *handle )
 {
     struct iovec vec;
     struct msghdr msghdr;
@@ -1100,7 +1114,7 @@ static inline NTSTATUS get_cached_fd( HANDLE handle, int *fd, enum server_fd_typ
 /***********************************************************************
  *           remove_fd_from_cache
  */
-static int remove_fd_from_cache( HANDLE handle )
+int remove_fd_from_cache( HANDLE handle )
 {
     unsigned int entry, idx = handle_to_index( handle, &entry );
     int fd = -1;
@@ -1837,12 +1851,17 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, HANDLE
         return result.dup_handle.status;
     }
 
+    /* hold fd_cache_mutex to prevent the fd from being added again between the
+     * call to remove_fd_from_cache and close_handle */
     server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
 
     /* always remove the cached fd; if the server request fails we'll just
      * retrieve it again */
     if (options & DUPLICATE_CLOSE_SOURCE)
+    {
         fd = remove_fd_from_cache( source );
+        close_inproc_sync_obj( source );
+    }
 
     SERVER_START_REQ( dup_handle )
     {
@@ -1908,14 +1927,15 @@ NTSTATUS WINAPI NtClose( HANDLE handle )
     if (HandleToLong( handle ) >= ~5 && HandleToLong( handle ) <= ~0)
         return STATUS_SUCCESS;
 
+    /* hold fd_cache_mutex to prevent the fd from being added again between the
+     * call to remove_fd_from_cache and close_handle */
     server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
 
     /* always remove the cached fd; if the server request fails we'll just
      * retrieve it again */
     fd = remove_fd_from_cache( handle );
 
-    if (do_esync())
-        esync_close( handle );
+    close_inproc_sync_obj( handle );
 
     SERVER_START_REQ( close_handle )
     {
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 66cdb09..f515865 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -30,9 +30,11 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <signal.h>
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
 #ifdef HAVE_SYS_SYSCALL_H
 #include <sys/syscall.h>
@@ -48,6 +50,8 @@
 #endif
 #include <string.h>
 #include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
@@ -57,6 +61,9 @@
 #ifdef HAVE_KQUEUE
 # include <sys/event.h>
 #endif
+#ifdef HAVE_LINUX_NTSYNC_H
+# include <linux/ntsync.h>
+#endif
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -66,7 +73,6 @@
 #include "wine/server.h"
 #include "wine/debug.h"
 #include "unix_private.h"
-#include "esync.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(sync);
 
@@ -75,9 +81,11 @@ HANDLE keyed_event = 0;
 static const char *debugstr_timeout( const LARGE_INTEGER *timeout )
 {
     if (!timeout) return "(infinite)";
-    return wine_dbgstr_longlong( timeout->QuadPart );
+    return wine_dbg_sprintf( "%lld.%07ld", (long long)(timeout->QuadPart / TICKSPERSEC),
+                             (long)(timeout->QuadPart % TICKSPERSEC) );
 }
 
+
 /* return a monotonic time counter, in Win32 ticks */
 static inline ULONGLONG monotonic_counter(void)
 {
@@ -302,6 +310,879 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at
 }
 
 
+#ifdef NTSYNC_IOC_EVENT_READ
+
+static int get_linux_sync_device(void)
+{
+    static LONG device = -2;
+
+    if (device == -2)
+    {
+        HANDLE handle;
+        int fd, needs_close;
+        NTSTATUS ret;
+
+        SERVER_START_REQ( get_linux_sync_device )
+        {
+            if (!(ret = wine_server_call( req ))) handle = wine_server_ptr_handle( reply->handle );
+        }
+        SERVER_END_REQ;
+
+        if (!ret)
+        {
+            if (!server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))
+            {
+                if (InterlockedCompareExchange( &device, fd, -2 ) != -2)
+                {
+                    /* someone beat us to it */
+                    if (needs_close) close( fd );
+                    NtClose( handle );
+                }
+                /* otherwise don't close the device */
+            }
+            else
+            {
+                InterlockedCompareExchange( &device, -1, -2 );
+                NtClose( handle );
+            }
+        }
+        else
+        {
+            InterlockedCompareExchange( &device, -1, -2 );
+        }
+    }
+    return device;
+}
+
+/* It's possible for synchronization primitives to remain alive even after being
+ * closed, because a thread is still waiting on them. It's rare in practice, and
+ * documented as being undefined behaviour by Microsoft, but it works, and some
+ * applications rely on it. This means we need to refcount handles, and defer
+ * deleting them on the server side until the refcount reaches zero. We do this
+ * by having each client process hold a handle to the in-process synchronization
+ * object, as well as a private refcount. When the client refcount reaches zero,
+ * it closes the handle; when all handles are closed, the server deletes the
+ * in-process synchronization object.
+ *
+ * We also need this for signal-and-wait. The signal and wait operations aren't
+ * atomic, but we can't perform the signal and then return STATUS_INVALID_HANDLE
+ * for the wait—we need to either do both operations or neither. That means we
+ * need to grab references to both objects, and prevent them from being
+ * destroyed before we're done with them.
+ *
+ * We want lookup of objects from the cache to be very fast; ideally, it should
+ * be lock-free. We achieve this by using atomic modifications to "refcount",
+ * and guaranteeing that all other fields are valid and correct *as long as*
+ * refcount is nonzero, and we store the entire structure in memory which will
+ * never be freed.
+ *
+ * This means that acquiring the object can't use a simple atomic increment; it
+ * has to use a compare-and-swap loop to ensure that it doesn't try to increment
+ * an object with a zero refcount. That's still leagues better than a real lock,
+ * though, and release can be a single atomic decrement.
+ *
+ * It also means that threads modifying the cache need to take a lock, to
+ * prevent other threads from writing to it concurrently.
+ *
+ * It's possible for an object currently in use (by a waiter) to be closed and
+ * the same handle immediately reallocated to a different object. This should be
+ * a very rare situation, and in that case we simply don't cache the handle.
+ */
+struct inproc_sync_cache_entry
+{
+    LONG refcount;
+    uint8_t type; /* enum inproc_sync_type, stored as uint8_t to save space */
+    bool closed;
+    unsigned int access;
+    /* handle to the underlying in-process sync object, stored as obj_handle_t
+     * to save space */
+    obj_handle_t handle;
+};
+
+
+/* Convenience structure describing an in-process sync object, filled by
+ * get_inproc_sync_obj(). That function will return a pointer to a cache entry;
+ * if the object could not be cached, it will point to the stack_cache member
+ * instead. */
+struct inproc_sync_obj
+{
+    struct inproc_sync_cache_entry *cache;
+    struct inproc_sync_cache_entry stack_cache;
+    int fd, needs_close;
+};
+
+static void release_cached_inproc_sync_obj( struct inproc_sync_cache_entry *cache )
+{
+    /* save the handle and fd now; as soon as the refcount hits 0 we cannot
+     * access the cache anymore */
+    HANDLE handle = wine_server_ptr_handle( cache->handle );
+    LONG refcount = InterlockedDecrement( &cache->refcount );
+
+    assert( refcount >= 0 );
+
+    if (!refcount)
+    {
+        NTSTATUS ret;
+        int fd;
+
+        /* we can't call NtClose here as we may be inside fd_cache_mutex */
+        fd = remove_fd_from_cache( handle );
+        if (fd >= 0) close( fd );
+        SERVER_START_REQ( close_handle )
+        {
+            req->handle = wine_server_obj_handle( handle );
+            ret = wine_server_call( req );
+        }
+        SERVER_END_REQ;
+
+        assert( !ret );
+    }
+}
+
+static void release_inproc_sync_obj( struct inproc_sync_obj *obj )
+{
+    if (obj->needs_close) close( obj->fd );
+    release_cached_inproc_sync_obj( obj->cache );
+}
+
+
+#define INPROC_SYNC_CACHE_BLOCK_SIZE  (65536 / sizeof(struct inproc_sync_cache_entry))
+#define INPROC_SYNC_CACHE_ENTRIES     128
+
+static struct inproc_sync_cache_entry *inproc_sync_cache[INPROC_SYNC_CACHE_ENTRIES];
+static struct inproc_sync_cache_entry inproc_sync_cache_initial_block[INPROC_SYNC_CACHE_BLOCK_SIZE];
+
+static inline unsigned int inproc_sync_handle_to_index( HANDLE handle, unsigned int *entry )
+{
+    unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1;
+    *entry = idx / INPROC_SYNC_CACHE_BLOCK_SIZE;
+    return idx % INPROC_SYNC_CACHE_BLOCK_SIZE;
+}
+
+
+static struct inproc_sync_cache_entry *cache_inproc_sync_obj( HANDLE handle, obj_handle_t inproc_sync,
+                                                              enum inproc_sync_type type, unsigned int access )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync_cache_entry *cache;
+    sigset_t sigset;
+    int refcount;
+
+    /* don't cache pseudo-handles; waiting on them is pointless anyway */
+    if ((ULONG)(ULONG_PTR)handle > 0xfffffffa)
+        return FALSE;
+
+    if (entry >= INPROC_SYNC_CACHE_ENTRIES)
+    {
+        FIXME( "too many allocated handles, not caching %p\n", handle );
+        return NULL;
+    }
+
+    if (!inproc_sync_cache[entry])  /* do we need to allocate a new block of entries? */
+    {
+        if (!entry) inproc_sync_cache[0] = inproc_sync_cache_initial_block;
+        else
+        {
+            static const size_t size = INPROC_SYNC_CACHE_BLOCK_SIZE * sizeof(struct inproc_sync_cache_entry);
+            void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE );
+            if (ptr == MAP_FAILED) return NULL;
+            if (InterlockedCompareExchangePointer( (void **)&inproc_sync_cache[entry], ptr, NULL ))
+                munmap( ptr, size ); /* someone beat us to it */
+        }
+    }
+
+    cache = &inproc_sync_cache[entry][idx];
+
+    /* Hold fd_cache_mutex instead of a separate mutex, to prevent the same
+     * race between this function and NtClose. That is, prevent the object from
+     * being cached again between close_inproc_sync_obj() and close_handle. */
+    server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+    if (InterlockedCompareExchange( &cache->refcount, 0, 0 ))
+    {
+        /* We lost the race with another thread trying to cache this object, or
+         * the handle is currently being used for another object (i.e. it was
+         * closed and then reused). We have no way of knowing which, and in the
+         * latter case we can't cache this object until the old one is
+         * completely destroyed, so always return failure. */
+        server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+        return NULL;
+    }
+
+    cache->handle = inproc_sync;
+    cache->type = type;
+    cache->access = access;
+    cache->closed = false;
+    /* Make sure we set the other members before the refcount; this store needs
+     * release semantics [paired with the load in get_cached_inproc_sync_obj()].
+     * Set the refcount to 2 (one for the handle, one for the caller). */
+    refcount = InterlockedExchange( &cache->refcount, 2 );
+    assert( !refcount );
+
+    server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
+
+    return cache;
+}
+
+
+/* returns the previous value */
+static inline LONG interlocked_inc_if_nonzero( LONG *dest )
+{
+    LONG val, tmp;
+    for (val = *dest;; val = tmp)
+    {
+        if (!val || (tmp = InterlockedCompareExchange( dest, val + 1, val )) == val)
+            break;
+    }
+    return val;
+}
+
+
+static struct inproc_sync_cache_entry *get_cached_inproc_sync_obj( HANDLE handle )
+{
+    unsigned int entry, idx = inproc_sync_handle_to_index( handle, &entry );
+    struct inproc_sync_cache_entry *cache;
+
+    if (entry >= INPROC_SYNC_CACHE_ENTRIES || !inproc_sync_cache[entry])
+        return NULL;
+
+    cache = &inproc_sync_cache[entry][idx];
+
+    /* this load needs acquire semantics [paired with the store in
+     * cache_inproc_sync_obj()] */
+    if (!interlocked_inc_if_nonzero( &cache->refcount ))
+        return NULL;
+
+    if (cache->closed)
+    {
+        /* The object is still being used, but "handle" has been closed. The
+         * handle value might have been reused for another object in the
+         * meantime, in which case we have to report that valid object, so
+         * force the caller to check the server. */
+        release_cached_inproc_sync_obj( cache );
+        return NULL;
+    }
+
+    return cache;
+}
+
+
+static BOOL inproc_sync_types_match( enum inproc_sync_type a, enum inproc_sync_type b )
+{
+    if (a == b) return TRUE;
+    if (a == INPROC_SYNC_AUTO_EVENT && b == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+    if (b == INPROC_SYNC_AUTO_EVENT && a == INPROC_SYNC_MANUAL_EVENT) return TRUE;
+    return FALSE;
+}
+
+
+static NTSTATUS get_inproc_sync_obj( HANDLE handle, enum inproc_sync_type desired_type,
+                                     ACCESS_MASK desired_access, struct inproc_sync_obj *obj )
+{
+    struct inproc_sync_cache_entry *cache;
+    obj_handle_t inproc_sync_handle;
+    enum inproc_sync_type type;
+    unsigned int access;
+    NTSTATUS ret;
+
+    /* We don't need the device right now, but if we can't access it, that
+     * means ntsync isn't available. Fail fast in that case. */
+    if (get_linux_sync_device() < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
+    if (!(cache = get_cached_inproc_sync_obj( handle )))
+    {
+        /* try to retrieve it from the server */
+        SERVER_START_REQ( get_linux_sync_obj )
+        {
+            req->handle = wine_server_obj_handle( handle );
+            if (!(ret = wine_server_call( req )))
+            {
+                inproc_sync_handle = reply->handle;
+                access = reply->access;
+                type = reply->type;
+            }
+        }
+        SERVER_END_REQ;
+
+        if (ret) return ret;
+
+        if (!(cache = cache_inproc_sync_obj( handle, inproc_sync_handle, type, access )))
+        {
+            cache = &obj->stack_cache;
+            cache->handle = inproc_sync_handle;
+            cache->type = type;
+            cache->access = access;
+            cache->closed = false;
+            cache->refcount = 1;
+        }
+    }
+
+    obj->cache = cache;
+
+    if ((ret = server_get_unix_fd( wine_server_ptr_handle( cache->handle ),
+                                   0, &obj->fd, &obj->needs_close, NULL, NULL )))
+        return ret;
+
+    if (desired_type != INPROC_SYNC_UNKNOWN && !inproc_sync_types_match( cache->type, desired_type ))
+    {
+        release_inproc_sync_obj( obj );
+        return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+
+    if ((cache->access & desired_access) != desired_access)
+    {
+        release_inproc_sync_obj( obj );
+        return STATUS_ACCESS_DENIED;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/* caller must hold fd_cache_mutex */
+void close_inproc_sync_obj( HANDLE handle )
+{
+    struct inproc_sync_cache_entry *cache = get_cached_inproc_sync_obj( handle );
+
+    if (cache)
+    {
+        cache->closed = true;
+        /* once for the reference we just grabbed, and once for the handle */
+        release_cached_inproc_sync_obj( cache );
+        release_cached_inproc_sync_obj( cache );
+    }
+}
+
+
+static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count )
+{
+    if (ioctl( obj, NTSYNC_IOC_SEM_RELEASE, &count ) < 0)
+    {
+        if (errno == EOVERFLOW)
+            return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
+        return errno_to_status( errno );
+    }
+    if (prev_count) *prev_count = count;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_MODIFY_STATE, &obj )))
+        return ret;
+
+    ret = linux_release_semaphore_obj( obj.fd, count, prev_count );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    struct ntsync_sem_args args = {0};
+
+    if (ioctl( obj, NTSYNC_IOC_SEM_READ, &args ) < 0)
+        return errno_to_status( errno );
+    info->CurrentCount = args.count;
+    info->MaximumCount = args.max;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_SEMAPHORE, SEMAPHORE_QUERY_STATE, &obj )))
+        return ret;
+
+    ret = linux_query_semaphore_obj( obj.fd, info );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state )
+{
+    __u32 prev;
+
+    if (ioctl( obj, NTSYNC_IOC_EVENT_SET, &prev ) < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+        return ret;
+
+    ret = linux_set_event_obj( obj.fd, prev_state );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state )
+{
+    __u32 prev;
+
+    if (ioctl( obj, NTSYNC_IOC_EVENT_RESET, &prev ) < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+        return ret;
+
+    ret = linux_reset_event_obj( obj.fd, prev_state );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state )
+{
+    __u32 prev;
+
+    if (ioctl( obj, NTSYNC_IOC_EVENT_PULSE, &prev ) < 0)
+        return errno_to_status( errno );
+    if (prev_state) *prev_state = prev;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_MODIFY_STATE, &obj )))
+        return ret;
+
+    ret = linux_pulse_event_obj( obj.fd, prev_state );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_query_event_obj( int obj, enum inproc_sync_type type, EVENT_BASIC_INFORMATION *info )
+{
+    struct ntsync_event_args args = {0};
+
+    if (ioctl( obj, NTSYNC_IOC_EVENT_READ, &args ) < 0)
+        return errno_to_status( errno );
+    info->EventType = (type == INPROC_SYNC_AUTO_EVENT) ? SynchronizationEvent : NotificationEvent;
+    info->EventState = args.signaled;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_AUTO_EVENT, EVENT_QUERY_STATE, &obj )))
+        return ret;
+
+    ret = linux_query_event_obj( obj.fd, obj.cache->type, info );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count )
+{
+    struct ntsync_mutex_args args = {0};
+
+    args.owner = GetCurrentThreadId();
+    if (ioctl( obj, NTSYNC_IOC_MUTEX_UNLOCK, &args ) < 0)
+    {
+        if (errno == EOVERFLOW)
+            return STATUS_MUTANT_LIMIT_EXCEEDED;
+        if (errno == EPERM)
+            return STATUS_MUTANT_NOT_OWNED;
+        return errno_to_status( errno );
+    }
+    if (prev_count) *prev_count = 1 - args.count;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, 0, &obj )))
+        return ret;
+
+    ret = linux_release_mutex_obj( obj.fd, prev_count );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+
+static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info )
+{
+    struct ntsync_mutex_args args = {0};
+
+    if (ioctl( obj, NTSYNC_IOC_MUTEX_READ, &args ) < 0)
+    {
+        if (errno == EOWNERDEAD)
+        {
+            info->AbandonedState = TRUE;
+            info->OwnedByCaller = FALSE;
+            info->CurrentCount = 1;
+            return STATUS_SUCCESS;
+        }
+        return errno_to_status( errno );
+    }
+    info->AbandonedState = FALSE;
+    info->OwnedByCaller = (args.owner == GetCurrentThreadId());
+    info->CurrentCount = 1 - args.count;
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+    struct inproc_sync_obj obj;
+    NTSTATUS ret;
+
+    if ((ret = get_inproc_sync_obj( handle, INPROC_SYNC_MUTEX, MUTANT_QUERY_STATE, &obj )))
+        return ret;
+
+    ret = linux_query_mutex_obj( obj.fd, info );
+
+    release_inproc_sync_obj( &obj );
+    return ret;
+}
+
+static void select_queue(void)
+{
+    SERVER_START_REQ( select_inproc_queue )
+    {
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static void unselect_queue( BOOL signaled )
+{
+    SERVER_START_REQ( unselect_inproc_queue )
+    {
+        req->signaled = signaled;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+
+static int get_inproc_alert_obj(void)
+{
+    struct ntdll_thread_data *data = ntdll_get_thread_data();
+    struct inproc_sync_obj obj;
+    HANDLE alert_handle;
+    unsigned int ret;
+
+    if (data->linux_alert_obj == -1)
+    {
+        SERVER_START_REQ( get_inproc_alert_event )
+        {
+            if ((ret = wine_server_call( req )))
+                ERR( "failed to get inproc alert event, status %#x\n", ret );
+            alert_handle = wine_server_ptr_handle( reply->handle );
+        }
+        SERVER_END_REQ;
+
+        if ((ret = get_inproc_sync_obj( alert_handle, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &obj )))
+            ERR( "failed to get inproc alert obj, status %#x\n", ret );
+        data->linux_alert_obj = obj.fd;
+        /* Don't close the server handle (or the fd). */
+    }
+
+    return data->linux_alert_obj;
+}
+
+static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs,
+                                 BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    struct ntsync_wait_args args = {0};
+    unsigned long request;
+    struct timespec now;
+    int ret;
+
+    if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE)
+    {
+        args.timeout = ~(__u64)0;
+    }
+    else if (timeout->QuadPart <= 0)
+    {
+        clock_gettime( CLOCK_MONOTONIC, &now );
+        args.timeout = (now.tv_sec * NSECPERSEC) + now.tv_nsec + (-timeout->QuadPart * 100);
+    }
+    else
+    {
+        args.timeout = (timeout->QuadPart * 100) - (SECS_1601_TO_1970 * NSECPERSEC);
+        args.flags |= NTSYNC_WAIT_REALTIME;
+    }
+
+    args.objs = (uintptr_t)objs;
+    args.count = count;
+    args.owner = GetCurrentThreadId();
+    args.index = ~0u;
+
+    if (alertable)
+        args.alert = get_inproc_alert_obj();
+
+    if (wait_any || count == 1)
+        request = NTSYNC_IOC_WAIT_ANY;
+    else
+        request = NTSYNC_IOC_WAIT_ALL;
+
+    do
+    {
+        ret = ioctl( device, request, &args );
+    } while (ret < 0 && errno == EINTR);
+
+    if (!ret)
+    {
+        if (args.index == count)
+        {
+            static const LARGE_INTEGER timeout;
+
+            ret = server_wait( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &timeout );
+            assert( ret == STATUS_USER_APC );
+            return ret;
+        }
+
+        return wait_any ? args.index : 0;
+    }
+    if (errno == EOWNERDEAD)
+        return STATUS_ABANDONED + (wait_any ? args.index : 0);
+    if (errno == ETIMEDOUT)
+        return STATUS_TIMEOUT;
+    return errno_to_status( errno );
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    struct inproc_sync_obj objs[64];
+    int device, fds[64];
+    HANDLE queue = NULL;
+    NTSTATUS ret;
+    DWORD i, j;
+
+    if ((device = get_linux_sync_device()) < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
+    for (i = 0; i < count; ++i)
+    {
+        if ((ret = get_inproc_sync_obj( handles[i], INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &objs[i] )))
+        {
+            for (j = 0; j < i; ++j)
+                release_inproc_sync_obj( &objs[j] );
+            return ret;
+        }
+        if (objs[i].cache->type == INPROC_SYNC_QUEUE)
+            queue = handles[i];
+
+        fds[i] = objs[i].fd;
+    }
+
+    /* It's common to wait on the message queue alone. Some applications wait
+     * on it in fast paths, with a zero timeout. Since we take two server calls
+     * instead of one when going through inproc_wait(), and since we only need
+     * to go through that path if we're waiting on other objects, just delegate
+     * to the server if we're only waiting on the message queue. */
+    if (count == 1 && queue)
+    {
+        release_inproc_sync_obj( &objs[0] );
+        return server_wait_for_object( handles[0], alertable, timeout );
+    }
+
+    if (queue) select_queue();
+
+    ret = linux_wait_objs( device, count, fds, wait_any, alertable, timeout );
+
+    if (queue) unselect_queue( handles[ret] == queue );
+
+    for (i = 0; i < count; ++i)
+        release_inproc_sync_obj( &objs[i] );
+
+    return ret;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+                                        BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    struct inproc_sync_obj signal_obj, wait_obj;
+    HANDLE queue = NULL;
+    NTSTATUS ret;
+    int device;
+
+    if ((device = get_linux_sync_device()) < 0)
+        return STATUS_NOT_IMPLEMENTED;
+
+    if ((ret = get_inproc_sync_obj( signal, INPROC_SYNC_UNKNOWN, 0, &signal_obj )))
+        return ret;
+
+    switch (signal_obj.cache->type)
+    {
+    case INPROC_SYNC_SEMAPHORE:
+        if (!(signal_obj.cache->access & SEMAPHORE_MODIFY_STATE))
+        {
+            release_inproc_sync_obj( &signal_obj );
+            return STATUS_ACCESS_DENIED;
+        }
+        break;
+
+    case INPROC_SYNC_AUTO_EVENT:
+    case INPROC_SYNC_MANUAL_EVENT:
+        if (!(signal_obj.cache->access & EVENT_MODIFY_STATE))
+        {
+            release_inproc_sync_obj( &signal_obj );
+            return STATUS_ACCESS_DENIED;
+        }
+        break;
+
+    case INPROC_SYNC_MUTEX:
+        break;
+
+    default:
+        /* can't be signaled */
+        release_inproc_sync_obj( &signal_obj );
+        return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+
+    if ((ret = get_inproc_sync_obj( wait, INPROC_SYNC_UNKNOWN, SYNCHRONIZE, &wait_obj )))
+    {
+        release_inproc_sync_obj( &signal_obj );
+        return ret;
+    }
+
+    if (wait_obj.cache->type == INPROC_SYNC_QUEUE)
+        queue = wait;
+
+    switch (signal_obj.cache->type)
+    {
+    case INPROC_SYNC_SEMAPHORE:
+        ret = linux_release_semaphore_obj( signal_obj.fd, 1, NULL );
+        break;
+
+    case INPROC_SYNC_AUTO_EVENT:
+    case INPROC_SYNC_MANUAL_EVENT:
+        ret = linux_set_event_obj( signal_obj.fd, NULL );
+        break;
+
+    case INPROC_SYNC_MUTEX:
+        ret = linux_release_mutex_obj( signal_obj.fd, NULL );
+        break;
+
+    default:
+        assert( 0 );
+        break;
+    }
+
+    if (!ret)
+    {
+        if (queue) select_queue();
+        ret = linux_wait_objs( device, 1, &wait_obj.fd, TRUE, alertable, timeout );
+        if (queue) unselect_queue( !ret );
+    }
+
+    release_inproc_sync_obj( &signal_obj );
+    release_inproc_sync_obj( &wait_obj );
+    return ret;
+}
+
+#else
+
+void close_inproc_sync_obj( HANDLE handle )
+{
+}
+
+static NTSTATUS inproc_release_semaphore( HANDLE handle, ULONG count, ULONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_semaphore( HANDLE handle, SEMAPHORE_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_set_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_reset_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_pulse_event( HANDLE handle, LONG *prev_state )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_event( HANDLE handle, EVENT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_release_mutex( HANDLE handle, LONG *prev_count )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_query_mutex( HANDLE handle, MUTANT_BASIC_INFORMATION *info )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_wait( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+                             BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS inproc_signal_and_wait( HANDLE signal, HANDLE wait,
+                                        BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif
+
+
 /******************************************************************************
  *              NtCreateSemaphore (NTDLL.@)
  */
@@ -312,13 +1193,13 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, initial %d, max %d\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", (int)initial, (int)max );
+
     *handle = 0;
     if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
-    if (do_esync())
-        return esync_create_semaphore( handle, access, attr, initial, max );
-
     SERVER_START_REQ( create_semaphore )
     {
         req->access  = access;
@@ -342,11 +1223,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC
 {
     unsigned int ret;
 
-    *handle = 0;
-
-    if (do_esync())
-        return esync_open_semaphore( handle, access, attr );
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
 
+    *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
     SERVER_START_REQ( open_semaphore )
@@ -383,8 +1262,11 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla
 
     if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
 
-    if (do_esync())
-        return esync_query_semaphore( handle, info, ret_len );
+    if ((ret = inproc_query_semaphore( handle, out )) != STATUS_NOT_IMPLEMENTED)
+    {
+        if (!ret && ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION);
+        return ret;
+    }
 
     SERVER_START_REQ( query_semaphore )
     {
@@ -408,8 +1290,10 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous
 {
     unsigned int ret;
 
-    if (do_esync())
-        return esync_release_semaphore( handle, count, previous );
+    TRACE( "handle %p, count %u, prev_count %p\n", handle, (int)count, previous );
+
+    if ((ret = inproc_release_semaphore( handle, count, previous )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
 
     SERVER_START_REQ( release_semaphore )
     {
@@ -435,12 +1319,11 @@ NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u, state %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type, state );
+
     *handle = 0;
     if (type != NotificationEvent && type != SynchronizationEvent) return STATUS_INVALID_PARAMETER;
-
-    if (do_esync())
-        return esync_create_event( handle, access, attr, type, state );
-
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
     SERVER_START_REQ( create_event )
@@ -466,12 +1349,11 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
-    if (do_esync())
-        return esync_open_event( handle, access, attr );
-
     SERVER_START_REQ( open_event )
     {
         req->access     = access;
@@ -492,11 +1374,12 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
  */
 NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
 {
-    /* This comment is a dummy to make sure this patch applies in the right place. */
     unsigned int ret;
 
-    if (do_esync())
-        return esync_set_event( handle );
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
+    if ((ret = inproc_set_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
 
     SERVER_START_REQ( event_op )
     {
@@ -515,12 +1398,12 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
  */
 NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
 {
-    /* This comment is a dummy to make sure this patch applies in the right place. */
     unsigned int ret;
 
-    if (do_esync())
-        return esync_reset_event( handle );
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
 
+    if ((ret = inproc_reset_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
 
     SERVER_START_REQ( event_op )
     {
@@ -551,8 +1434,10 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
 {
     unsigned int ret;
 
-    if (do_esync())
-        return esync_pulse_event( handle );
+    TRACE( "handle %p, prev_state %p\n", handle, prev_state );
+
+    if ((ret = inproc_pulse_event( handle, prev_state )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
 
     SERVER_START_REQ( event_op )
     {
@@ -585,8 +1470,11 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class,
 
     if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
 
-    if (do_esync())
-        return esync_query_event( handle, info, ret_len );
+    if ((ret = inproc_query_event( handle, out )) != STATUS_NOT_IMPLEMENTED)
+    {
+        if (!ret && ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION);
+        return ret;
+    }
 
     SERVER_START_REQ( query_event )
     {
@@ -613,11 +1501,10 @@ NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT
     data_size_t len;
     struct object_attributes *objattr;
 
-    *handle = 0;
-
-    if (do_esync())
-        return esync_create_mutex( handle, access, attr, owned );
+    TRACE( "access %#x, name %s, owned %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", owned );
 
+    *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
     SERVER_START_REQ( create_mutex )
@@ -642,12 +1529,11 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
-    if (do_esync())
-        return esync_open_mutex( handle, access, attr );
-
     SERVER_START_REQ( open_mutex )
     {
         req->access  = access;
@@ -670,8 +1556,10 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count )
 {
     unsigned int ret;
 
-    if (do_esync())
-        return esync_release_mutex( handle, prev_count );
+    TRACE( "handle %p, prev_count %p\n", handle, prev_count );
+
+    if ((ret = inproc_release_mutex( handle, prev_count )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
 
     SERVER_START_REQ( release_mutex )
     {
@@ -703,8 +1591,11 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class,
 
     if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
 
-    if (do_esync())
-        return esync_query_mutex( handle, info, ret_len );
+    if ((ret = inproc_query_mutex( handle, out )) != STATUS_NOT_IMPLEMENTED)
+    {
+        if (!ret && ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION);
+        return ret;
+    }
 
     SERVER_START_REQ( query_mutex )
     {
@@ -1475,6 +2366,9 @@ NTSTATUS WINAPI NtCreateTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, type %u\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", type );
+
     *handle = 0;
     if (type != NotificationTimer && type != SynchronizationTimer) return STATUS_INVALID_PARAMETER;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
@@ -1502,6 +2396,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1555,6 +2451,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state )
 {
     unsigned int ret;
 
+    TRACE( "handle %p, state %p\n", handle, state );
+
     SERVER_START_REQ( cancel_timer )
     {
         req->handle = wine_server_obj_handle( handle );
@@ -1623,20 +2521,29 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO
 {
     union select_op select_op;
     UINT i, flags = SELECT_INTERRUPTIBLE;
+    unsigned int ret;
 
     if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
 
-    if (do_esync())
+    if (TRACE_ON(sync))
     {
-        NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout );
-        if (ret != STATUS_NOT_IMPLEMENTED)
-            return ret;
+        TRACE( "wait_any %u, alertable %u, handles {%p", wait_any, alertable, handles[0] );
+        for (i = 1; i < count; i++) TRACE( ", %p", handles[i] );
+        TRACE( "}, timeout %s\n", debugstr_timeout(timeout) );
+    }
+
+    if ((ret = inproc_wait( count, handles, wait_any, alertable, timeout )) != STATUS_NOT_IMPLEMENTED)
+    {
+        TRACE( "-> %#x\n", ret );
+        return ret;
     }
 
     if (alertable) flags |= SELECT_ALERTABLE;
     select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL;
     for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] );
-    return server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout );
+    ret = server_wait( &select_op, offsetof( union select_op, wait.handles[count] ), flags, timeout );
+    TRACE( "-> %#x\n", ret );
+    return ret;
 }
 
 
@@ -1657,12 +2564,15 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait,
 {
     union select_op select_op;
     UINT flags = SELECT_INTERRUPTIBLE;
+    NTSTATUS ret;
 
-    if (do_esync())
-        return esync_signal_and_wait( signal, wait, alertable, timeout );
+    TRACE( "signal %p, wait %p, alertable %u, timeout %s\n", signal, wait, alertable, debugstr_timeout(timeout) );
 
     if (!signal) return STATUS_INVALID_HANDLE;
 
+    if ((ret = inproc_signal_and_wait( signal, wait, alertable, timeout )) != STATUS_NOT_IMPLEMENTED)
+        return ret;
+
     if (alertable) flags |= SELECT_ALERTABLE;
     select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT;
     select_op.signal_and_wait.wait = wine_server_obj_handle( wait );
@@ -1905,6 +2815,9 @@ NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
     data_size_t len;
     struct object_attributes *objattr;
 
+    TRACE( "access %#x, name %s, flags %#x\n", (int)access,
+           attr ? debugstr_us(attr->ObjectName) : "(null)", (int)flags );
+
     *handle = 0;
     if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
 
@@ -1929,6 +2842,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE
 {
     unsigned int ret;
 
+    TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" );
+
     *handle = 0;
     if ((ret = validate_open_object_attributes( attr ))) return ret;
 
@@ -1955,6 +2870,8 @@ NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key,
     union select_op select_op;
     UINT flags = SELECT_INTERRUPTIBLE;
 
+    TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) );
+
     if (!handle) handle = keyed_event;
     if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
     if (alertable) flags |= SELECT_ALERTABLE;
@@ -1974,6 +2891,8 @@ NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key,
     union select_op select_op;
     UINT flags = SELECT_INTERRUPTIBLE;
 
+    TRACE( "handle %p, key %p, alertable %u, timeout %s\n", handle, key, alertable, debugstr_timeout(timeout) );
+
     if (!handle) handle = keyed_event;
     if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
     if (alertable) flags |= SELECT_ALERTABLE;
@@ -2118,7 +3037,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
     }
     SERVER_END_REQ;
     if (status != STATUS_PENDING) return status;
-    if (!timeout || timeout->QuadPart) status = NtWaitForSingleObject( wait_handle, FALSE, timeout );
+    if (!timeout || timeout->QuadPart) status = server_wait_for_object( wait_handle, FALSE, timeout );
     else                               status = STATUS_TIMEOUT;
     if (status != WAIT_OBJECT_0) return status;
 
@@ -2182,7 +3101,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
         assert( status == STATUS_USER_APC );
         goto done;
     }
-    if (!timeout || timeout->QuadPart) status = NtWaitForSingleObject( wait_handle, alertable, timeout );
+    if (!timeout || timeout->QuadPart) status = server_wait_for_object( wait_handle, alertable, timeout );
     else                               status = STATUS_TIMEOUT;
     if (status != WAIT_OBJECT_0) goto done;
 
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index b64a7dd..a66ae7d 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1801,7 +1801,7 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma
 
     if (ret == STATUS_PENDING)
     {
-        NtWaitForSingleObject( context_handle, FALSE, NULL );
+        server_wait_for_object( context_handle, FALSE, NULL );
 
         SERVER_START_REQ( get_thread_context )
         {
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index bcefd25..e714089 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -103,7 +103,6 @@ struct ntdll_thread_data
 {
     void              *cpu_data[16];  /* reserved for CPU-specific data */
     void              *kernel_stack;  /* stack for thread startup and kernel syscalls */
-    int                esync_apc_fd;  /* fd to wait on for user APCs */
     int                request_fd;    /* fd for sending server requests */
     int                reply_fd;      /* fd for receiving server replies */
     int                wait_fd[2];    /* fd for sleeping server requests */
@@ -113,6 +112,7 @@ struct ntdll_thread_data
     PRTL_THREAD_START_ROUTINE start;  /* thread entry point */
     void              *param;         /* thread entry point parameter */
     void              *jmp_buf;       /* setjmp buffer for exception handling */
+    int                linux_alert_obj; /* fd for the linux in-process alert event */
 };
 
 C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) );
@@ -208,6 +208,8 @@ extern NTSTATUS load_start_exe( WCHAR **image, void **module );
 extern ULONG_PTR redirect_arm64ec_rva( void *module, ULONG_PTR rva, const IMAGE_ARM64EC_METADATA *metadata );
 extern void start_server( BOOL debug );
 
+extern pthread_mutex_t fd_cache_mutex;
+
 extern unsigned int server_call_unlocked( void *req_ptr );
 extern void server_enter_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
 extern void server_leave_uninterrupted_section( pthread_mutex_t *mutex, sigset_t *sigset );
@@ -215,10 +217,12 @@ extern unsigned int server_select( const union select_op *select_op, data_size_t
                                    timeout_t abs_timeout, struct context_data *context, struct user_apc *user_apc );
 extern unsigned int server_wait( const union select_op *select_op, data_size_t size, UINT flags,
                                  const LARGE_INTEGER *timeout );
+extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout );
 extern unsigned int server_queue_process_apc( HANDLE process, const union apc_call *call,
                                               union apc_result *result );
 extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd,
                                int *needs_close, enum server_fd_type *type, unsigned int *options );
+extern int remove_fd_from_cache( HANDLE handle );
 extern void wine_server_send_fd( int fd );
 extern void process_exit_wrapper( int status ) DECLSPEC_NORETURN;
 extern size_t server_init_process(void);
@@ -381,6 +385,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args );
 
 extern void dbg_init(void);
 
+extern void close_inproc_sync_obj( HANDLE handle );
+
 extern NTSTATUS call_user_apc_dispatcher( CONTEXT *context_ptr, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3,
                                           PNTAPCFUNC func, NTSTATUS status );
 extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context );
@@ -389,6 +395,7 @@ extern void call_raise_user_exception_dispatcher(void);
 #define IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE 0x0010 /* Wine extension */
 
 #define TICKSPERSEC 10000000
+#define NSECPERSEC 1000000000
 #define SECS_1601_TO_1970  ((369 * 365 + 89) * (ULONGLONG)86400)
 
 static inline ULONGLONG ticks_from_time_t( time_t time )
@@ -454,7 +461,7 @@ static inline struct async_data server_async( HANDLE handle, struct async_fileio
 
 static inline NTSTATUS wait_async( HANDLE handle, BOOL alertable )
 {
-    return NtWaitForSingleObject( handle, alertable, NULL );
+    return server_wait_for_object( handle, alertable, NULL );
 }
 
 static inline BOOL in_wow64_call(void)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index 39afda3..03daf0d 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -3709,11 +3709,11 @@ static TEB *init_teb( void *ptr, BOOL is_wow )
     teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer;
     teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
     thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch;
-    thread_data->esync_apc_fd = -1;
     thread_data->request_fd = -1;
     thread_data->reply_fd   = -1;
     thread_data->wait_fd[0] = -1;
     thread_data->wait_fd[1] = -1;
+    thread_data->linux_alert_obj = -1;
     list_add_head( &teb_list, &thread_data->entry );
     return teb;
 }
diff --git a/include/config.h.in b/include/config.h.in
index 83bdd28..c190e93 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -192,6 +192,9 @@
 /* Define to 1 if you have the <linux/major.h> header file. */
 #undef HAVE_LINUX_MAJOR_H
 
+/* Define to 1 if you have the <linux/ntsync.h> header file. */
+#undef HAVE_LINUX_NTSYNC_H
+
 /* Define to 1 if you have the <linux/param.h> header file. */
 #undef HAVE_LINUX_PARAM_H
 
@@ -528,9 +531,6 @@
 /* Define to 1 if you have the <sys/epoll.h> header file. */
 #undef HAVE_SYS_EPOLL_H
 
-/* Define to 1 if you have the <sys/eventfd.h> header file. */
-#undef HAVE_SYS_EVENTFD_H
-
 /* Define to 1 if you have the <sys/event.h> header file. */
 #undef HAVE_SYS_EVENT_H
 
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 7da4531..11635b7 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -5908,75 +5908,68 @@ struct get_next_thread_reply
     char __pad_12[4];
 };
 
-enum esync_type
+enum inproc_sync_type
 {
-    ESYNC_SEMAPHORE = 1,
-    ESYNC_AUTO_EVENT,
-    ESYNC_MANUAL_EVENT,
-    ESYNC_MUTEX,
-    ESYNC_AUTO_SERVER,
-    ESYNC_MANUAL_SERVER,
-    ESYNC_QUEUE,
+    INPROC_SYNC_UNKNOWN,
+    INPROC_SYNC_AUTO_EVENT,
+    INPROC_SYNC_MANUAL_EVENT,
+    INPROC_SYNC_SEMAPHORE,
+    INPROC_SYNC_MUTEX,
+    INPROC_SYNC_AUTO_SERVER,
+    INPROC_SYNC_MANUAL_SERVER,
+    INPROC_SYNC_QUEUE,
 };
 
 
-struct create_esync_request
+
+struct get_linux_sync_device_request
 {
     struct request_header __header;
-    unsigned int access;
-    int          initval;
-    int          type;
-    int          max;
-    /* VARARG(objattr,object_attributes); */
-    char __pad_28[4];
+    char __pad_12[4];
 };
-struct create_esync_reply
+struct get_linux_sync_device_reply
 {
     struct reply_header __header;
     obj_handle_t handle;
-    int          type;
-    unsigned int shm_idx;
-    char __pad_20[4];
+    char __pad_12[4];
 };
 
-struct open_esync_request
+
+
+struct get_linux_sync_obj_request
 {
     struct request_header __header;
-    unsigned int access;
-    unsigned int attributes;
-    obj_handle_t rootdir;
-    int          type;
-    /* VARARG(name,unicode_str); */
-    char __pad_28[4];
+    obj_handle_t handle;
 };
-struct open_esync_reply
+struct get_linux_sync_obj_reply
 {
     struct reply_header __header;
     obj_handle_t handle;
     int          type;
-    unsigned int shm_idx;
+    unsigned int access;
     char __pad_20[4];
 };
 
 
-struct get_esync_fd_request
+
+struct select_inproc_queue_request
 {
     struct request_header __header;
-    obj_handle_t handle;
+    char __pad_12[4];
 };
-struct get_esync_fd_reply
+struct select_inproc_queue_reply
 {
     struct reply_header __header;
-    int          type;
-    unsigned int shm_idx;
 };
 
-struct esync_msgwait_request
+
+
+struct unselect_inproc_queue_request
 {
     struct request_header __header;
-    int          in_msgwait;
+    int          signaled;
 };
-struct esync_msgwait_reply
+struct unselect_inproc_queue_reply
 {
     struct reply_header __header;
 };
@@ -5997,14 +5990,17 @@ struct set_keyboard_repeat_reply
 };
 
 
-struct get_esync_apc_fd_request
+
+struct get_inproc_alert_event_request
 {
     struct request_header __header;
     char __pad_12[4];
 };
-struct get_esync_apc_fd_reply
+struct get_inproc_alert_event_reply
 {
     struct reply_header __header;
+    obj_handle_t handle;
+    char __pad_12[4];
 };
 
 
@@ -6303,12 +6299,12 @@ enum request
     REQ_resume_process,
     REQ_get_next_process,
     REQ_get_next_thread,
-    REQ_create_esync,
-    REQ_open_esync,
-    REQ_get_esync_fd,
-    REQ_esync_msgwait,
+    REQ_get_linux_sync_device,
+    REQ_get_linux_sync_obj,
+    REQ_select_inproc_queue,
+    REQ_unselect_inproc_queue,
     REQ_set_keyboard_repeat,
-    REQ_get_esync_apc_fd,
+    REQ_get_inproc_alert_event,
     REQ_NB_REQUESTS
 };
 
@@ -6609,12 +6605,12 @@ union generic_request
     struct resume_process_request resume_process_request;
     struct get_next_process_request get_next_process_request;
     struct get_next_thread_request get_next_thread_request;
-    struct create_esync_request create_esync_request;
-    struct open_esync_request open_esync_request;
-    struct get_esync_fd_request get_esync_fd_request;
-    struct esync_msgwait_request esync_msgwait_request;
+    struct get_linux_sync_device_request get_linux_sync_device_request;
+    struct get_linux_sync_obj_request get_linux_sync_obj_request;
+    struct select_inproc_queue_request select_inproc_queue_request;
+    struct unselect_inproc_queue_request unselect_inproc_queue_request;
     struct set_keyboard_repeat_request set_keyboard_repeat_request;
-    struct get_esync_apc_fd_request get_esync_apc_fd_request;
+    struct get_inproc_alert_event_request get_inproc_alert_event_request;
 };
 union generic_reply
 {
@@ -6913,14 +6909,14 @@ union generic_reply
     struct resume_process_reply resume_process_reply;
     struct get_next_process_reply get_next_process_reply;
     struct get_next_thread_reply get_next_thread_reply;
-    struct create_esync_reply create_esync_reply;
-    struct open_esync_reply open_esync_reply;
-    struct get_esync_fd_reply get_esync_fd_reply;
-    struct esync_msgwait_reply esync_msgwait_reply;
+    struct get_linux_sync_device_reply get_linux_sync_device_reply;
+    struct get_linux_sync_obj_reply get_linux_sync_obj_reply;
+    struct select_inproc_queue_reply select_inproc_queue_reply;
+    struct unselect_inproc_queue_reply unselect_inproc_queue_reply;
     struct set_keyboard_repeat_reply set_keyboard_repeat_reply;
-    struct get_esync_apc_fd_reply get_esync_apc_fd_reply;
+    struct get_inproc_alert_event_reply get_inproc_alert_event_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 857
+#define SERVER_PROTOCOL_VERSION 867
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 9632c4e..91ec73d 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -11,12 +11,12 @@ SOURCES = \
 	debugger.c \
 	device.c \
 	directory.c \
-	esync.c \
 	event.c \
 	fd.c \
 	file.c \
 	handle.c \
 	hook.c \
+	inproc_sync.c \
 	mach.c \
 	mailslot.c \
 	main.c \
diff --git a/server/async.c b/server/async.c
index 9768a49..e8b50a7 100644
--- a/server/async.c
+++ b/server/async.c
@@ -78,7 +78,6 @@ static const struct object_ops async_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     async_signaled,            /* signaled */
-    NULL,                      /* get_esync_fd */
     async_satisfied,           /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -91,6 +90,7 @@ static const struct object_ops async_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     async_destroy              /* destroy */
 };
@@ -699,7 +699,6 @@ static const struct object_ops iosb_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -712,6 +711,7 @@ static const struct object_ops iosb_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     iosb_destroy              /* destroy */
 };
diff --git a/server/atom.c b/server/atom.c
index d9824de..04951e6 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -79,7 +79,6 @@ static const struct object_ops atom_table_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -92,6 +91,7 @@ static const struct object_ops atom_table_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     atom_table_destroy            /* destroy */
 };
diff --git a/server/change.c b/server/change.c
index 682c7f7..d42e9eb 100644
--- a/server/change.c
+++ b/server/change.c
@@ -112,7 +112,6 @@ static const struct object_ops dir_ops =
     add_queue,                /* add_queue */
     remove_queue,             /* remove_queue */
     default_fd_signaled,      /* signaled */
-    default_fd_get_esync_fd,  /* get_esync_fd */
     no_satisfied,             /* satisfied */
     no_signal,                /* signal */
     dir_get_fd,               /* get_fd */
@@ -125,6 +124,7 @@ static const struct object_ops dir_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    default_fd_get_inproc_sync, /* get_inproc_sync */
     dir_close_handle,         /* close_handle */
     dir_destroy               /* destroy */
 };
diff --git a/server/clipboard.c b/server/clipboard.c
index 0df7fd2..a2258ae 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -76,7 +76,6 @@ static const struct object_ops clipboard_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -89,6 +88,7 @@ static const struct object_ops clipboard_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     clipboard_destroy             /* destroy */
 };
diff --git a/server/completion.c b/server/completion.c
index 3d75015..1c4389f 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -77,6 +77,7 @@ struct completion
     struct list    wait_queue;
     unsigned int   depth;
     int            closed;
+    struct inproc_sync *inproc_sync;
 };
 
 static void completion_wait_dump( struct object*, int );
@@ -92,7 +93,6 @@ static const struct object_ops completion_wait_ops =
     add_queue,                      /* add_queue */
     remove_queue,                   /* remove_queue */
     completion_wait_signaled,       /* signaled */
-    NULL,                           /* get_esync_fd */
     completion_wait_satisfied,      /* satisfied */
     no_signal,                      /* signal */
     no_get_fd,                      /* get_fd */
@@ -105,6 +105,7 @@ static const struct object_ops completion_wait_ops =
     NULL,                           /* unlink_name */
     no_open_file,                   /* open_file */
     no_kernel_obj_list,             /* get_kernel_obj_list */
+    no_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                /* close_handle */
     completion_wait_destroy         /* destroy */
 };
@@ -156,6 +157,7 @@ static void completion_wait_satisfied( struct object *obj, struct wait_queue_ent
 
 static void completion_dump( struct object*, int );
 static int completion_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *completion_get_inproc_sync( struct object *obj );
 static int completion_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void completion_destroy( struct object * );
 
@@ -167,7 +169,6 @@ static const struct object_ops completion_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     completion_signaled,       /* signaled */
-    NULL,                      /* get_esync_fd */
     no_satisfied,              /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -180,6 +181,7 @@ static const struct object_ops completion_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    completion_get_inproc_sync,/* get_inproc_sync */
     completion_close_handle,   /* close_handle */
     completion_destroy         /* destroy */
 };
@@ -193,6 +195,7 @@ static void completion_destroy( struct object *obj)
     {
         free( tmp );
     }
+    if (completion->inproc_sync) release_object( completion->inproc_sync );
 }
 
 static void completion_dump( struct object *obj, int verbose )
@@ -230,6 +233,7 @@ static int completion_close_handle( struct object *obj, struct process *process,
     }
     completion->closed = 1;
     wake_up( obj, 0 );
+    set_inproc_event( completion->inproc_sync );
     return 1;
 }
 
@@ -263,6 +267,16 @@ static struct completion_wait *create_completion_wait( struct thread *thread )
     return wait;
 }
 
+static struct inproc_sync *completion_get_inproc_sync( struct object *obj )
+{
+    struct completion *completion = (struct completion *)obj;
+
+    if (!completion->inproc_sync)
+        completion->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !list_empty( &completion->queue ) );
+    if (completion->inproc_sync) grab_object( completion->inproc_sync );
+    return completion->inproc_sync;
+}
+
 static struct completion *create_completion( struct object *root, const struct unicode_str *name,
                                              unsigned int attr, unsigned int concurrent,
                                              const struct security_descriptor *sd )
@@ -277,6 +291,7 @@ static struct completion *create_completion( struct object *root, const struct u
             list_init( &completion->wait_queue );
             completion->depth = 0;
             completion->closed = 0;
+            completion->inproc_sync = NULL;
         }
     }
 
@@ -309,7 +324,11 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_
         wake_up( &wait->obj, 1 );
         if (list_empty( &completion->queue )) return;
     }
-    if (!list_empty( &completion->queue )) wake_up( &completion->obj, 0 );
+    if (!list_empty( &completion->queue ))
+    {
+        wake_up( &completion->obj, 0 );
+        set_inproc_event( completion->inproc_sync );
+    }
 }
 
 /* create a completion */
@@ -410,6 +429,8 @@ DECL_HANDLER(remove_completion)
         reply->information = msg->information;
         free( msg );
         reply->wait_handle = 0;
+        if (list_empty( &completion->queue ))
+            reset_inproc_event( completion->inproc_sync );
     }
 
     release_object( completion );
diff --git a/server/console.c b/server/console.c
index c85eb98..5244898 100644
--- a/server/console.c
+++ b/server/console.c
@@ -41,7 +41,6 @@
 #include "wincon.h"
 #include "winternl.h"
 #include "wine/condrv.h"
-#include "esync.h"
 
 struct screen_buffer;
 
@@ -62,6 +61,7 @@ struct console
     struct fd                   *fd;            /* for bare console, attached input fd */
     struct async_queue           ioctl_q;       /* ioctl queue */
     struct async_queue           read_q;        /* read queue */
+    struct inproc_sync          *inproc_sync;   /* in-process synchronization object */
 };
 
 static void console_dump( struct object *obj, int verbose );
@@ -73,6 +73,7 @@ static struct object *console_lookup_name( struct object *obj, struct unicode_st
 static struct object *console_open_file( struct object *obj, unsigned int access,
                                          unsigned int sharing, unsigned int options );
 static int console_add_queue( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *console_get_inproc_sync( struct object *obj );
 
 static const struct object_ops console_ops =
 {
@@ -82,7 +83,6 @@ static const struct object_ops console_ops =
     console_add_queue,                /* add_queue */
     remove_queue,                     /* remove_queue */
     console_signaled,                 /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     console_get_fd,                   /* get_fd */
@@ -95,6 +95,7 @@ static const struct object_ops console_ops =
     NULL,                             /* unlink_name */
     console_open_file,                /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    console_get_inproc_sync,          /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_destroy                   /* destroy */
 };
@@ -132,27 +133,27 @@ struct console_host_ioctl
 
 struct console_server
 {
-    struct object         obj;         /* object header */
-    struct fd            *fd;          /* pseudo-fd for ioctls */
-    struct console       *console;     /* attached console */
-    struct list           queue;       /* ioctl queue */
-    struct list           read_queue;  /* blocking read queue */
+    struct object         obj;            /* object header */
+    struct fd            *fd;             /* pseudo-fd for ioctls */
+    struct console       *console;        /* attached console */
+    struct list           queue;          /* ioctl queue */
+    struct list           read_queue;     /* blocking read queue */
     unsigned int          busy : 1;       /* flag if server processing an ioctl */
     unsigned int          once_input : 1; /* flag if input thread has already been requested */
-    int                   term_fd;     /* UNIX terminal fd */
-    struct termios        termios;     /* original termios */
-    int                   esync_fd;
+    int                   term_fd;        /* UNIX terminal fd */
+    struct termios        termios;        /* original termios */
+    struct inproc_sync   *inproc_sync;    /* in-process synchronization object */
 };
 
 static void console_server_dump( struct object *obj, int verbose );
 static void console_server_destroy( struct object *obj );
 static int console_server_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int console_server_get_esync_fd( struct object *obj, enum esync_type *type );
 static struct fd *console_server_get_fd( struct object *obj );
 static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name,
                                                 unsigned int attr, struct object *root );
 static struct object *console_server_open_file( struct object *obj, unsigned int access,
                                                 unsigned int sharing, unsigned int options );
+static struct inproc_sync *console_server_get_inproc_sync( struct object *obj );
 
 static const struct object_ops console_server_ops =
 {
@@ -162,7 +163,6 @@ static const struct object_ops console_server_ops =
     add_queue,                        /* add_queue */
     remove_queue,                     /* remove_queue */
     console_server_signaled,          /* signaled */
-    console_server_get_esync_fd,      /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     console_server_get_fd,            /* get_fd */
@@ -175,6 +175,7 @@ static const struct object_ops console_server_ops =
     NULL,                             /* unlink_name */
     console_server_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    console_server_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_server_destroy            /* destroy */
 };
@@ -223,6 +224,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry
 static struct fd *screen_buffer_get_fd( struct object *obj );
 static struct object *screen_buffer_open_file( struct object *obj, unsigned int access,
                                                unsigned int sharing, unsigned int options );
+static struct inproc_sync *screen_buffer_get_inproc_sync( struct object *obj );
 
 static const struct object_ops screen_buffer_ops =
 {
@@ -232,7 +234,6 @@ static const struct object_ops screen_buffer_ops =
     screen_buffer_add_queue,          /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     NULL,                             /* satisfied */
     no_signal,                        /* signal */
     screen_buffer_get_fd,             /* get_fd */
@@ -245,6 +246,7 @@ static const struct object_ops screen_buffer_ops =
     NULL,                             /* unlink_name */
     screen_buffer_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    screen_buffer_get_inproc_sync,    /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     screen_buffer_destroy             /* destroy */
 };
@@ -282,7 +284,6 @@ static const struct object_ops console_device_ops =
     no_add_queue,                     /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
@@ -295,6 +296,7 @@ static const struct object_ops console_device_ops =
     default_unlink_name,              /* unlink_name */
     console_device_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     no_destroy                        /* destroy */
 };
@@ -310,6 +312,7 @@ static struct object *console_input_open_file( struct object *obj, unsigned int
                                                unsigned int sharing, unsigned int options );
 static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry );
 static struct fd *console_input_get_fd( struct object *obj );
+static struct inproc_sync *console_input_get_inproc_sync( struct object *obj );
 static void console_input_destroy( struct object *obj );
 
 static const struct object_ops console_input_ops =
@@ -320,7 +323,6 @@ static const struct object_ops console_input_ops =
     console_input_add_queue,          /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     console_input_get_fd,             /* get_fd */
@@ -333,6 +335,7 @@ static const struct object_ops console_input_ops =
     default_unlink_name,              /* unlink_name */
     console_input_open_file,          /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    console_input_get_inproc_sync,    /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_input_destroy             /* destroy */
 };
@@ -368,6 +371,7 @@ static int console_output_add_queue( struct object *obj, struct wait_queue_entry
 static struct fd *console_output_get_fd( struct object *obj );
 static struct object *console_output_open_file( struct object *obj, unsigned int access,
                                                 unsigned int sharing, unsigned int options );
+static struct inproc_sync *console_output_get_inproc_sync( struct object *obj );
 static void console_output_destroy( struct object *obj );
 
 static const struct object_ops console_output_ops =
@@ -378,7 +382,6 @@ static const struct object_ops console_output_ops =
     console_output_add_queue,         /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     console_output_get_fd,            /* get_fd */
@@ -391,6 +394,7 @@ static const struct object_ops console_output_ops =
     default_unlink_name,              /* unlink_name */
     console_output_open_file,         /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    console_output_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     console_output_destroy            /* destroy */
 };
@@ -437,7 +441,6 @@ static const struct object_ops console_connection_ops =
     no_add_queue,                     /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     console_connection_get_fd,        /* get_fd */
@@ -450,6 +453,7 @@ static const struct object_ops console_connection_ops =
     default_unlink_name,              /* unlink_name */
     console_connection_open_file,     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     console_connection_close_handle,  /* close_handle */
     console_connection_destroy        /* destroy */
 };
@@ -549,6 +553,7 @@ static struct object *create_console(void)
     console->server        = NULL;
     console->fd            = NULL;
     console->last_id       = 0;
+    console->inproc_sync   = NULL;
     init_async_queue( &console->ioctl_q );
     init_async_queue( &console->read_q );
 
@@ -588,6 +593,7 @@ static int queue_host_ioctl( struct console_server *server, unsigned int code, u
     }
     list_add_tail( &server->queue, &ioctl->entry );
     wake_up( &server->obj, 0 );
+    set_inproc_event( server->inproc_sync );
     if (async) set_error( STATUS_PENDING );
     return 1;
 }
@@ -600,8 +606,6 @@ static void disconnect_console_server( struct console_server *server )
         list_remove( &call->entry );
         console_host_ioctl_terminate( call, STATUS_CANCELLED );
     }
-    if (do_esync())
-        esync_clear( server->esync_fd );
     while (!list_empty( &server->read_queue ))
     {
         struct console_host_ioctl *call = LIST_ENTRY( list_head( &server->read_queue ), struct console_host_ioctl, entry );
@@ -622,6 +626,7 @@ static void disconnect_console_server( struct console_server *server )
         server->console->server = NULL;
         server->console = NULL;
         wake_up( &server->obj, 0 );
+        set_inproc_event( server->inproc_sync );
     }
 }
 
@@ -776,6 +781,8 @@ static void console_destroy( struct object *obj )
     free_async_queue( &console->read_q );
     if (console->fd)
         release_object( console->fd );
+
+    if (console->inproc_sync) release_object( console->inproc_sync );
 }
 
 static struct object *create_console_connection( struct console *console )
@@ -829,6 +836,16 @@ static struct object *console_open_file( struct object *obj, unsigned int access
     return grab_object( obj );
 }
 
+static struct inproc_sync *console_get_inproc_sync( struct object *obj )
+{
+    struct console *console = (struct console *)obj;
+
+    if (!console->inproc_sync)
+        console->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, console->signaled );
+    if (console->inproc_sync) grab_object( console->inproc_sync );
+    return console->inproc_sync;
+}
+
 static void screen_buffer_dump( struct object *obj, int verbose )
 {
     struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
@@ -878,6 +895,17 @@ static struct fd *screen_buffer_get_fd( struct object *obj )
     return NULL;
 }
 
+static struct inproc_sync *screen_buffer_get_inproc_sync( struct object *obj )
+{
+    struct screen_buffer *screen_buffer = (struct screen_buffer *)obj;
+    if (!screen_buffer->input)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return NULL;
+    }
+    return console_get_inproc_sync( &screen_buffer->input->obj );
+}
+
 static void console_server_dump( struct object *obj, int verbose )
 {
     assert( obj->ops == &console_server_ops );
@@ -890,7 +918,7 @@ static void console_server_destroy( struct object *obj )
     assert( obj->ops == &console_server_ops );
     disconnect_console_server( server );
     if (server->fd) release_object( server->fd );
-    if (do_esync()) close( server->esync_fd );
+    if (server->inproc_sync) release_object( server->inproc_sync );
 }
 
 static struct object *console_server_lookup_name( struct object *obj, struct unicode_str *name,
@@ -938,13 +966,6 @@ static int console_server_signaled( struct object *obj, struct wait_queue_entry
     return !server->console || !list_empty( &server->queue );
 }
 
-static int console_server_get_esync_fd( struct object *obj, enum esync_type *type )
-{
-    struct console_server *server = (struct console_server*)obj;
-    *type = ESYNC_MANUAL_SERVER;
-    return server->esync_fd;
-}
-
 static struct fd *console_server_get_fd( struct object* obj )
 {
     struct console_server *server = (struct console_server*)obj;
@@ -958,6 +979,17 @@ static struct object *console_server_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static struct inproc_sync *console_server_get_inproc_sync( struct object *obj )
+{
+    struct console_server *server = (struct console_server *)obj;
+    int signaled = !server->console || !list_empty( &server->queue );
+
+    if (!server->inproc_sync)
+        server->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, signaled );
+    if (server->inproc_sync) grab_object( server->inproc_sync );
+    return server->inproc_sync;
+}
+
 static struct object *create_console_server( void )
 {
     struct console_server *server;
@@ -969,6 +1001,7 @@ static struct object *create_console_server( void )
     server->term_fd    = -1;
     list_init( &server->queue );
     list_init( &server->read_queue );
+    server->inproc_sync = NULL;
     server->fd = alloc_pseudo_fd( &console_server_fd_ops, &server->obj, FILE_SYNCHRONOUS_IO_NONALERT );
     if (!server->fd)
     {
@@ -976,10 +1009,6 @@ static struct object *create_console_server( void )
         return NULL;
     }
     allow_fd_caching(server->fd);
-    server->esync_fd = -1;
-
-    if (do_esync())
-        server->esync_fd = esync_create_fd( 0, 0 );
 
     return &server->obj;
 }
@@ -1438,6 +1467,16 @@ static struct object *console_input_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static struct inproc_sync *console_input_get_inproc_sync( struct object *obj )
+{
+    if (!current->process->console)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return NULL;
+    }
+    return console_get_inproc_sync( &current->process->console->obj );
+}
+
 static void console_input_destroy( struct object *obj )
 {
     struct console_input *console_input = (struct console_input *)obj;
@@ -1510,6 +1549,16 @@ static struct object *console_output_open_file( struct object *obj, unsigned int
     return grab_object( obj );
 }
 
+static struct inproc_sync *console_output_get_inproc_sync( struct object *obj )
+{
+    if (!current->process->console || !current->process->console->active)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+        return NULL;
+    }
+    return console_get_inproc_sync( &current->process->console->obj );
+}
+
 static void console_output_destroy( struct object *obj )
 {
     struct console_output *console_output = (struct console_output *)obj;
@@ -1567,11 +1616,16 @@ DECL_HANDLER(get_next_console_request)
 
     if (!server->console->renderer) server->console->renderer = current;
 
-    if (!req->signal) server->console->signaled = 0;
+    if (!req->signal)
+    {
+        server->console->signaled = 0;
+        reset_inproc_event( server->console->inproc_sync );
+    }
     else if (!server->console->signaled)
     {
         server->console->signaled = 1;
         wake_up( &server->console->obj, 0 );
+        set_inproc_event( server->console->inproc_sync );
     }
 
     if (req->read)
@@ -1593,8 +1647,8 @@ DECL_HANDLER(get_next_console_request)
         /* set result of previous ioctl */
         ioctl = LIST_ENTRY( list_head( &server->queue ), struct console_host_ioctl, entry );
         list_remove( &ioctl->entry );
-        if (do_esync() && list_empty( &server->queue ))
-            esync_clear( server->esync_fd );
+        if (list_empty( &server->queue ))
+            reset_inproc_event( server->inproc_sync );
     }
 
     if (ioctl)
@@ -1680,8 +1734,9 @@ DECL_HANDLER(get_next_console_request)
     {
         set_error( STATUS_PENDING );
     }
-    if (do_esync() && list_empty( &server->queue ))
-        esync_clear( server->esync_fd );
+
+    if (list_empty( &server->queue ))
+        reset_inproc_event( server->inproc_sync );
 
     release_object( server );
 }
diff --git a/server/debugger.c b/server/debugger.c
index 0c01ec1..3544917 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -71,6 +71,7 @@ struct debug_obj
     struct object        obj;         /* object header */
     struct list          event_queue; /* pending events queue */
     unsigned int         flags;       /* debug flags */
+    struct inproc_sync  *inproc_sync; /* in-process synchronization object */
 };
 
 
@@ -86,7 +87,6 @@ static const struct object_ops debug_event_ops =
     add_queue,                     /* add_queue */
     remove_queue,                  /* remove_queue */
     debug_event_signaled,          /* signaled */
-    NULL,                          /* get_esync_fd */
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
@@ -99,12 +99,14 @@ static const struct object_ops debug_event_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     debug_event_destroy            /* destroy */
 };
 
 static void debug_obj_dump( struct object *obj, int verbose );
 static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *debug_obj_get_inproc_sync( struct object *obj );
 static void debug_obj_destroy( struct object *obj );
 
 static const struct object_ops debug_obj_ops =
@@ -115,7 +117,6 @@ static const struct object_ops debug_obj_ops =
     add_queue,                     /* add_queue */
     remove_queue,                  /* remove_queue */
     debug_obj_signaled,            /* signaled */
-    NULL,                          /* get_esync_fd */
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
@@ -128,6 +129,7 @@ static const struct object_ops debug_obj_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    debug_obj_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     debug_obj_destroy              /* destroy */
 };
@@ -255,6 +257,7 @@ static void link_event( struct debug_obj *debug_obj, struct debug_event *event )
         /* grab reference since debugger could be killed while trying to wake up */
         grab_object( debug_obj );
         wake_up( &debug_obj->obj, 0 );
+        set_inproc_event( debug_obj->inproc_sync );
         release_object( debug_obj );
     }
 }
@@ -267,6 +270,7 @@ static void resume_event( struct debug_obj *debug_obj, struct debug_event *event
     {
         grab_object( debug_obj );
         wake_up( &debug_obj->obj, 0 );
+        set_inproc_event( debug_obj->inproc_sync );
         release_object( debug_obj );
     }
 }
@@ -332,6 +336,17 @@ static int debug_obj_signaled( struct object *obj, struct wait_queue_entry *entr
     return find_event_to_send( debug_obj ) != NULL;
 }
 
+static struct inproc_sync *debug_obj_get_inproc_sync( struct object *obj )
+{
+    struct debug_obj *debug_obj = (struct debug_obj *)obj;
+    int signaled = find_event_to_send( debug_obj ) != NULL;
+
+    if (!debug_obj->inproc_sync)
+        debug_obj->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, signaled );
+    if (debug_obj->inproc_sync) grab_object( debug_obj->inproc_sync );
+    return debug_obj->inproc_sync;
+}
+
 static void debug_obj_destroy( struct object *obj )
 {
     struct list *ptr;
@@ -344,6 +359,8 @@ static void debug_obj_destroy( struct object *obj )
     /* free all pending events */
     while ((ptr = list_head( &debug_obj->event_queue )))
         unlink_event( debug_obj, LIST_ENTRY( ptr, struct debug_event, entry ));
+
+    if (debug_obj->inproc_sync) release_object( debug_obj->inproc_sync );
 }
 
 struct debug_obj *get_debug_obj( struct process *process, obj_handle_t handle, unsigned int access )
@@ -363,6 +380,7 @@ static struct debug_obj *create_debug_obj( struct object *root, const struct uni
         {
             debug_obj->flags = flags;
             list_init( &debug_obj->event_queue );
+            debug_obj->inproc_sync = NULL;
         }
     }
     return debug_obj;
@@ -571,6 +589,9 @@ DECL_HANDLER(wait_debug_event)
         reply->tid = get_thread_id( event->sender );
         alloc_event_handles( event, current->process );
         set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) ));
+
+        if (!find_event_to_send( debug_obj ))
+            reset_inproc_event( debug_obj->inproc_sync );
     }
     else
     {
diff --git a/server/device.c b/server/device.c
index 7782ef4..ef3ab50 100644
--- a/server/device.c
+++ b/server/device.c
@@ -38,7 +38,6 @@
 #include "handle.h"
 #include "request.h"
 #include "process.h"
-#include "esync.h"
 
 /* IRP object */
 
@@ -67,7 +66,6 @@ static const struct object_ops irp_call_ops =
     no_add_queue,                     /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     NULL,                             /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
@@ -80,6 +78,7 @@ static const struct object_ops irp_call_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     irp_call_destroy                  /* destroy */
 };
@@ -94,12 +93,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 inproc_sync    *inproc_sync;    /* in-process synchronization object */
 };
 
 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 inproc_sync *device_manager_get_inproc_sync( struct object *obj );
 static void device_manager_destroy( struct object *obj );
 
 static const struct object_ops device_manager_ops =
@@ -110,7 +109,6 @@ static const struct object_ops device_manager_ops =
     add_queue,                        /* add_queue */
     remove_queue,                     /* remove_queue */
     device_manager_signaled,          /* signaled */
-    device_manager_get_esync_fd,      /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
@@ -123,6 +121,7 @@ static const struct object_ops device_manager_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    device_manager_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     device_manager_destroy            /* destroy */
 };
@@ -168,7 +167,6 @@ static const struct object_ops device_ops =
     no_add_queue,                     /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
@@ -181,6 +179,7 @@ static const struct object_ops device_ops =
     default_unlink_name,              /* unlink_name */
     device_open_file,                 /* open_file */
     device_get_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     device_destroy                    /* destroy */
 };
@@ -221,7 +220,6 @@ static const struct object_ops device_file_ops =
     add_queue,                        /* add_queue */
     remove_queue,                     /* remove_queue */
     default_fd_signaled,              /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     device_file_get_fd,               /* get_fd */
@@ -234,6 +232,7 @@ static const struct object_ops device_file_ops =
     NULL,                             /* unlink_name */
     no_open_file,                     /* open_file */
     device_file_get_kernel_obj_list,  /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,       /* get_inproc_sync */
     device_file_close_handle,         /* close_handle */
     device_file_destroy               /* destroy */
 };
@@ -425,7 +424,12 @@ static void add_irp_to_queue( struct device_manager *manager, struct irp_call *i
     irp->thread = thread ? (struct thread *)grab_object( thread ) : NULL;
     if (irp->file) list_add_tail( &irp->file->requests, &irp->dev_entry );
     list_add_tail( &manager->requests, &irp->mgr_entry );
-    if (list_head( &manager->requests ) == &irp->mgr_entry) wake_up( &manager->obj, 0 );  /* first one */
+    if (list_head( &manager->requests ) == &irp->mgr_entry)
+    {
+        /* first one */
+        wake_up( &manager->obj, 0 );
+        set_inproc_event( manager->inproc_sync );
+    }
 }
 
 static struct object *device_open_file( struct object *obj, unsigned int access,
@@ -755,13 +759,13 @@ static void delete_file( struct device_file *file )
     /* terminate all pending requests */
     LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry )
     {
-        if (do_esync() && file->device->manager && list_empty( &file->device->manager->requests ))
-            esync_clear( file->device->manager->esync_fd );
-
         list_remove( &irp->mgr_entry );
         set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 );
     }
 
+    if (list_empty( &file->device->manager->requests ))
+        reset_inproc_event( file->device->manager->inproc_sync );
+
     release_object( file );
 }
 
@@ -793,11 +797,14 @@ 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 inproc_sync *device_manager_get_inproc_sync( struct object *obj )
 {
     struct device_manager *manager = (struct device_manager *)obj;
-    *type = ESYNC_MANUAL_SERVER;
-    return manager->esync_fd;
+
+    if (!manager->inproc_sync)
+        manager->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !list_empty( &manager->requests ) );
+    if (manager->inproc_sync) grab_object( manager->inproc_sync );
+    return manager->inproc_sync;
 }
 
 static void device_manager_destroy( struct object *obj )
@@ -835,8 +842,7 @@ static void device_manager_destroy( struct object *obj )
         release_object( irp );
     }
 
-    if (do_esync())
-        close( manager->esync_fd );
+    if (manager->inproc_sync) release_object( manager->inproc_sync );
 }
 
 static struct device_manager *create_device_manager(void)
@@ -846,12 +852,10 @@ static struct device_manager *create_device_manager(void)
     if ((manager = alloc_object( &device_manager_ops )))
     {
         manager->current_call = NULL;
+        manager->inproc_sync = NULL;
         list_init( &manager->devices );
         list_init( &manager->requests );
         wine_rb_init( &manager->kernel_objects, compare_kernel_object );
-
-        if (do_esync())
-            manager->esync_fd = esync_create_fd( 0, 0 );
     }
     return manager;
 }
@@ -1038,12 +1042,13 @@ DECL_HANDLER(get_next_device_request)
                 }
                 list_remove( &irp->mgr_entry );
                 list_init( &irp->mgr_entry );
+
+                if (list_empty( &manager->requests ))
+                    reset_inproc_event( manager->inproc_sync );
+
                 /* we already own the object if it's only on manager queue */
                 if (irp->file) grab_object( irp );
                 manager->current_call = irp;
-
-                if (do_esync() && list_empty( &manager->requests ))
-                    esync_clear( manager->esync_fd );
             }
             else close_handle( current->process, reply->next );
         }
diff --git a/server/directory.c b/server/directory.c
index 2894f76..40cb9b9 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -69,7 +69,6 @@ static const struct object_ops object_type_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -82,6 +81,7 @@ static const struct object_ops object_type_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     no_destroy                    /* destroy */
 };
@@ -120,7 +120,6 @@ static const struct object_ops directory_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -133,6 +132,7 @@ static const struct object_ops directory_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     directory_destroy             /* destroy */
 };
diff --git a/server/esync.c b/server/esync.c
deleted file mode 100644
index e193f61..0000000
--- a/server/esync.c
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- * eventfd-based synchronization objects
- *
- * Copyright (C) 2018 Zebediah Figura
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "config.h"
-
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdarg.h>
-#ifdef HAVE_SYS_EVENTFD_H
-# include <sys/eventfd.h>
-#endif
-#include <sys/mman.h>
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#include <unistd.h>
-
-#include "ntstatus.h"
-#define WIN32_NO_STATUS
-#include "windef.h"
-#include "winternl.h"
-
-#include "handle.h"
-#include "request.h"
-#include "file.h"
-#include "esync.h"
-
-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];
-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)
-{
-    close( shm_fd );
-    if (shm_unlink( shm_name ) == -1)
-        perror( "shm_unlink" );
-}
-
-void esync_init(void)
-{
-    struct stat st;
-
-    if (fstat( config_dir_fd, &st ) == -1)
-        fatal_error( "cannot stat config dir\n" );
-
-    if (st.st_ino != (unsigned long)st.st_ino)
-        sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino );
-    else
-        sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino );
-
-    shm_unlink( shm_name );
-
-    shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 );
-    if (shm_fd == -1)
-        perror( "shm_open" );
-
-    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" );
-
-    fprintf( stderr, "esync: up and running.\n" );
-
-    atexit( shm_cleanup );
-}
-
-static struct list mutex_list = LIST_INIT(mutex_list);
-
-struct esync
-{
-    struct object   obj;            /* object header */
-    int             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 unsigned int esync_map_access( struct object *obj, unsigned int access );
-static void esync_destroy( struct object *obj );
-
-const struct object_ops esync_ops =
-{
-    sizeof(struct esync),      /* size */
-    &no_type,                  /* type */
-    esync_dump,                /* dump */
-    no_add_queue,              /* add_queue */
-    NULL,                      /* remove_queue */
-    NULL,                      /* signaled */
-    esync_get_esync_fd,        /* get_esync_fd */
-    NULL,                      /* satisfied */
-    no_signal,                 /* signal */
-    no_get_fd,                 /* get_fd */
-    esync_map_access,          /* map_access */
-    default_get_sd,            /* get_sd */
-    default_set_sd,            /* set_sd */
-    default_get_full_name,     /* get_full_name */
-    no_lookup_name,            /* lookup_name */
-    directory_link_name,       /* link_name */
-    default_unlink_name,       /* unlink_name */
-    no_open_file,              /* open_file */
-    no_kernel_obj_list,        /* get_kernel_obj_list */
-    no_close_handle,           /* close_handle */
-    esync_destroy              /* destroy */
-};
-
-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 );
-}
-
-static int esync_get_esync_fd( struct object *obj, enum esync_type *type )
-{
-    struct esync *esync = (struct esync *)obj;
-    *type = esync->type;
-    return esync->fd;
-}
-
-static unsigned int esync_map_access( struct object *obj, unsigned int access )
-{
-    /* Sync objects have the same flags. */
-    if (access & GENERIC_READ)    access |= STANDARD_RIGHTS_READ | EVENT_QUERY_STATE;
-    if (access & GENERIC_WRITE)   access |= STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE;
-    if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE;
-    if (access & GENERIC_ALL)     access |= STANDARD_RIGHTS_ALL | EVENT_QUERY_STATE | EVENT_MODIFY_STATE;
-    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
-}
-
-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 );
-}
-
-static int type_matches( enum esync_type type1, enum esync_type type2 )
-{
-    return (type1 == type2) ||
-           ((type1 == ESYNC_AUTO_EVENT || type1 == ESYNC_MANUAL_EVENT) &&
-            (type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT));
-}
-
-static void *get_shm( unsigned int idx )
-{
-    int entry  = (idx * 8) / pagesize;
-    int offset = (idx * 8) % pagesize;
-
-    if (entry >= shm_addrs_size)
-    {
-        int new_size = max(shm_addrs_size * 2, entry + 1);
-
-        if (!(shm_addrs = realloc( shm_addrs, new_size * 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, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) );
-
-        shm_addrs_size = new_size;
-    }
-
-    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 (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr ))
-            munmap( addr, pagesize ); /* someone beat us to it */
-    }
-
-    return (void *)((unsigned long)shm_addrs[entry] + offset);
-}
-
-struct semaphore
-{
-    int max;
-    int count;
-};
-C_ASSERT(sizeof(struct semaphore) == 8);
-
-struct mutex
-{
-    DWORD tid;
-    int count;    /* recursion count */
-};
-C_ASSERT(sizeof(struct mutex) == 8);
-
-struct event
-{
-    int signaled;
-    int locked;
-};
-C_ASSERT(sizeof(struct event) == 8);
-
-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;
-
-            /* initialize it if it didn't already exist */
-            esync->fd = eventfd( initval, flags );
-            if (esync->fd == -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 */
-            while (esync->shm_idx * 8 >= shm_size)
-            {
-                /* Better expand the shm section. */
-                shm_size += pagesize;
-                if (ftruncate( shm_fd, shm_size ) == -1)
-                {
-                    fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
-                             shm_name, (long)shm_size );
-                    perror( "ftruncate" );
-                }
-            }
-
-            /* Initialize the shared memory portion. We want to do this on the
-             * server side to avoid a potential though unlikely race whereby
-             * the same object is opened and used between the time it's created
-             * and the time its shared memory portion is initialized. */
-            switch (type)
-            {
-            case ESYNC_SEMAPHORE:
-            {
-                struct semaphore *semaphore = get_shm( esync->shm_idx );
-                semaphore->max = max;
-                semaphore->count = initval;
-                break;
-            }
-            case ESYNC_AUTO_EVENT:
-            case ESYNC_MANUAL_EVENT:
-            {
-                struct event *event = get_shm( esync->shm_idx );
-                event->signaled = initval ? 1 : 0;
-                event->locked = 0;
-                break;
-            }
-            case ESYNC_MUTEX:
-            {
-                struct mutex *mutex = get_shm( esync->shm_idx );
-                mutex->tid = initval ? 0 : current->id;
-                mutex->count = initval ? 0 : 1;
-                list_add_tail( &mutex_list, &esync->mutex_entry );
-                break;
-            }
-            default:
-                assert( 0 );
-            }
-        }
-        else
-        {
-            /* validate the type */
-            if (!type_matches( type, esync->type ))
-            {
-                release_object( &esync->obj );
-                set_error( STATUS_OBJECT_TYPE_MISMATCH );
-                return NULL;
-            }
-        }
-    }
-    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 )
-{
-#ifdef HAVE_SYS_EVENTFD_H
-    int fd;
-
-    fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK );
-    if (fd == -1)
-        perror( "eventfd" );
-
-    return fd;
-#else
-    return -1;
-#endif
-}
-
-/* Wake up a specific fd. */
-void esync_wake_fd( int fd )
-{
-    static const uint64_t value = 1;
-
-    if (write( fd, &value, sizeof(value) ) == -1)
-        perror( "esync: write" );
-}
-
-/* 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 );
-    }
-}
-
-void esync_clear( int fd )
-{
-    uint64_t value;
-
-    /* we don't care about the return value */
-    read( fd, &value, sizeof(value) );
-}
-
-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 );
-    assert( event != NULL );
-
-    if (debug_level)
-        fprintf( stderr, "esync_set_event() fd=%d\n", esync->fd );
-
-    if (esync->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Acquire the spinlock. */
-        while (__sync_val_compare_and_swap( &event->locked, 0, 1 ))
-            small_pause();
-    }
-
-    if (!__atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))
-    {
-        if (write( esync->fd, &value, sizeof(value) ) == -1)
-            perror( "esync: write" );
-    }
-
-    if (esync->type == ESYNC_MANUAL_EVENT)
-    {
-        /* 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 );
-
-    if (debug_level)
-        fprintf( stderr, "esync_reset_event() fd=%d\n", esync->fd );
-
-    if (esync->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Acquire the spinlock. */
-        while (__sync_val_compare_and_swap( &event->locked, 0, 1 ))
-            small_pause();
-    }
-
-    /* Only bother signaling the fd if we weren't already signaled. */
-    if (__atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ))
-    {
-        /* we don't care about the return value */
-        read( esync->fd, &value, sizeof(value) );
-    }
-
-    if (esync->type == ESYNC_MANUAL_EVENT)
-    {
-        /* Release the spinlock. */
-        event->locked = 0;
-    }
-}
-
-void esync_abandon_mutexes( struct thread *thread )
-{
-    struct esync *esync;
-
-    LIST_FOR_EACH_ENTRY( esync, &mutex_list, struct esync, mutex_entry )
-    {
-        struct mutex *mutex = get_shm( esync->shm_idx );
-
-        if (mutex->tid == thread->id)
-        {
-            if (debug_level)
-                fprintf( stderr, "esync_abandon_mutexes() fd=%d\n", esync->fd );
-            mutex->tid = ~0;
-            mutex->count = 0;
-            esync_wake_fd( esync->fd );
-        }
-    }
-}
-
-DECL_HANDLER(create_esync)
-{
-    struct esync *esync;
-    struct unicode_str name;
-    struct object *root;
-    const struct security_descriptor *sd;
-    const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root );
-
-    if (!do_esync())
-    {
-        set_error( STATUS_NOT_IMPLEMENTED );
-        return;
-    }
-
-    if (!req->type)
-    {
-        set_error( STATUS_INVALID_PARAMETER );
-        return;
-    }
-
-    if (!objattr) return;
-
-    if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->max, req->type, sd )))
-    {
-        if (get_error() == STATUS_OBJECT_NAME_EXISTS)
-            reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );
-        else
-            reply->handle = alloc_handle_no_access_check( current->process, esync,
-                                                          req->access, objattr->attributes );
-
-        reply->type = esync->type;
-        reply->shm_idx = esync->shm_idx;
-        send_client_fd( current->process, esync->fd, reply->handle );
-        release_object( esync );
-    }
-
-    if (root) release_object( root );
-}
-
-DECL_HANDLER(open_esync)
-{
-    struct unicode_str name = get_req_unicode_str();
-
-    reply->handle = open_object( current->process, req->rootdir, req->access,
-                                 &esync_ops, &name, req->attributes );
-
-    /* send over the fd */
-    if (reply->handle)
-    {
-        struct esync *esync;
-
-        if (!(esync = (struct esync *)get_handle_obj( current->process, reply->handle,
-                                                      0, &esync_ops )))
-            return;
-
-        if (!type_matches( req->type, esync->type ))
-        {
-            set_error( STATUS_OBJECT_TYPE_MISMATCH );
-            release_object( esync );
-            return;
-        }
-
-        reply->type = esync->type;
-        reply->shm_idx = esync->shm_idx;
-
-        send_client_fd( current->process, esync->fd, reply->handle );
-        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)
-{
-    struct object *obj;
-    enum esync_type type;
-    int 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 );
-        reply->type = type;
-        if (obj->ops == &esync_ops)
-        {
-            struct esync *esync = (struct esync *)obj;
-            reply->shm_idx = esync->shm_idx;
-        }
-        else
-            reply->shm_idx = 0;
-        send_client_fd( current->process, fd, req->handle );
-    }
-    else
-    {
-        if (debug_level)
-        {
-            fprintf( stderr, "%04x: esync: can't wait on object: ", current->id );
-            obj->ops->dump( obj, 0 );
-        }
-        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 );
-}
diff --git a/server/esync.h b/server/esync.h
deleted file mode 100644
index d39f4ef..0000000
--- a/server/esync.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * eventfd-based synchronization objects
- *
- * Copyright (C) 2018 Zebediah Figura
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include <unistd.h>
-
-extern int do_esync(void);
-void esync_init(void);
-int esync_create_fd( int initval, int flags );
-void esync_wake_fd( int fd );
-void esync_wake_up( struct object *obj );
-void esync_clear( int fd );
-
-struct esync;
-
-extern const struct object_ops esync_ops;
-void esync_set_event( struct esync *esync );
-void esync_reset_event( struct esync *esync );
-void esync_abandon_mutexes( struct thread *thread );
diff --git a/server/event.c b/server/event.c
index 28a5620..5f25ad6 100644
--- a/server/event.c
+++ b/server/event.c
@@ -35,7 +35,6 @@
 #include "thread.h"
 #include "request.h"
 #include "security.h"
-#include "esync.h"
 
 static const WCHAR event_name[] = {'E','v','e','n','t'};
 
@@ -57,15 +56,15 @@ 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 inproc_sync *inproc_sync;/* in-process synchronization object */
 };
 
 static void event_dump( struct object *obj, int verbose );
 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 int event_signal( struct object *obj, unsigned int access);
 static struct list *event_get_kernel_obj_list( struct object *obj );
+static struct inproc_sync *event_get_inproc_sync( struct object *obj );
 static void event_destroy( struct object *obj );
 
 static const struct object_ops event_ops =
@@ -76,7 +75,6 @@ static const struct object_ops event_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     event_signaled,            /* signaled */
-    event_get_esync_fd,        /* get_esync_fd */
     event_satisfied,           /* satisfied */
     event_signal,              /* signal */
     no_get_fd,                 /* get_fd */
@@ -89,6 +87,7 @@ static const struct object_ops event_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     event_get_kernel_obj_list, /* get_kernel_obj_list */
+    event_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     event_destroy              /* destroy */
 };
@@ -111,10 +110,13 @@ struct type_descr keyed_event_type =
 struct keyed_event
 {
     struct object  obj;             /* object header */
+    struct inproc_sync *inproc_sync;/* in-process synchronization object */
 };
 
 static void keyed_event_dump( struct object *obj, int verbose );
 static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *keyed_event_get_inproc_sync( struct object *obj );
+static void keyed_event_destroy( struct object *obj );
 
 static const struct object_ops keyed_event_ops =
 {
@@ -124,7 +126,6 @@ static const struct object_ops keyed_event_ops =
     add_queue,                   /* add_queue */
     remove_queue,                /* remove_queue */
     keyed_event_signaled,        /* signaled */
-    NULL,                        /* get_esync_fd */
     no_satisfied,                /* satisfied */
     no_signal,                   /* signal */
     no_get_fd,                   /* get_fd */
@@ -137,8 +138,9 @@ static const struct object_ops keyed_event_ops =
     default_unlink_name,         /* unlink_name */
     no_open_file,                /* open_file */
     no_kernel_obj_list,          /* get_kernel_obj_list */
+    keyed_event_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,             /* close_handle */
-    no_destroy                   /* destroy */
+    keyed_event_destroy          /* destroy */
 };
 
 
@@ -156,9 +158,7 @@ struct event *create_event( struct object *root, const struct unicode_str *name,
             list_init( &event->kernel_object );
             event->manual_reset = manual_reset;
             event->signaled     = initial_state;
-
-            if (do_esync())
-                event->esync_fd = esync_create_fd( initial_state, 0 );
+            event->inproc_sync  = NULL;
         }
     }
     return event;
@@ -166,10 +166,6 @@ struct event *create_event( struct object *root, const struct unicode_str *name,
 
 struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access )
 {
-    struct object *obj;
-    if (do_esync() && (obj = get_handle_obj( process, handle, access, &esync_ops)))
-        return (struct event *)obj; /* even though it's not an event */
-
     return (struct event *)get_handle_obj( process, handle, access, &event_ops );
 }
 
@@ -183,28 +179,16 @@ static void pulse_event( struct event *event )
 
 void set_event( struct event *event )
 {
-    if (do_esync() && event->obj.ops == &esync_ops)
-    {
-        esync_set_event( (struct esync *)event );
-        return;
-    }
-
     event->signaled = 1;
     /* wake up all waiters if manual reset, a single one otherwise */
     wake_up( &event->obj, !event->manual_reset );
+    set_inproc_event( event->inproc_sync );
 }
 
 void reset_event( struct event *event )
 {
-    if (do_esync() && event->obj.ops == &esync_ops)
-    {
-        esync_reset_event( (struct esync *)event );
-        return;
-    }
     event->signaled = 0;
-
-    if (do_esync())
-        esync_clear( event->esync_fd );
+    reset_inproc_event( event->inproc_sync );
 }
 
 static void event_dump( struct object *obj, int verbose )
@@ -222,13 +206,6 @@ 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 )
-{
-    struct event *event = (struct event *)obj;
-    *type = event->manual_reset ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER;
-    return event->esync_fd;
-}
-
 static void event_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct event *event = (struct event *)obj;
@@ -257,12 +234,28 @@ static struct list *event_get_kernel_obj_list( struct object *obj )
     return &event->kernel_object;
 }
 
+static struct inproc_sync *event_get_inproc_sync( struct object *obj )
+{
+    struct event *event = (struct event *)obj;
+
+    /* This state will always be the state that the event was created with.
+     * We could create the inproc_sync at creation time to make this clearer,
+     * but some broken programs create hundreds of thousands of handles which
+     * they never use, and we want to avoid wasting memory or fds in that case. */
+    if (!event->inproc_sync)
+    {
+        enum inproc_sync_type type = event->manual_reset ? INPROC_SYNC_MANUAL_EVENT : INPROC_SYNC_AUTO_EVENT;
+        event->inproc_sync = create_inproc_event( type, event->signaled );
+    }
+    if (event->inproc_sync) grab_object( event->inproc_sync );
+    return event->inproc_sync;
+}
+
 static void event_destroy( struct object *obj )
 {
     struct event *event = (struct event *)obj;
 
-    if (do_esync())
-        close( event->esync_fd );
+    if (event->inproc_sync) release_object( event->inproc_sync );
 }
 
 struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name,
@@ -275,6 +268,7 @@ struct keyed_event *create_keyed_event( struct object *root, const struct unicod
         if (get_error() != STATUS_OBJECT_NAME_EXISTS)
         {
             /* initialize it if it didn't already exist */
+            event->inproc_sync = NULL;
         }
     }
     return event;
@@ -318,6 +312,23 @@ static int keyed_event_signaled( struct object *obj, struct wait_queue_entry *en
     return 0;
 }
 
+static struct inproc_sync *keyed_event_get_inproc_sync( struct object *obj )
+{
+    struct keyed_event *event = (struct keyed_event *)obj;
+
+    if (!event->inproc_sync)
+        event->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, 1 );
+    if (event->inproc_sync) grab_object( event->inproc_sync );
+    return event->inproc_sync;
+}
+
+static void keyed_event_destroy( struct object *obj )
+{
+    struct keyed_event *event = (struct keyed_event *)obj;
+
+    if (event->inproc_sync) release_object( event->inproc_sync );
+}
+
 /* create an event */
 DECL_HANDLER(create_event)
 {
diff --git a/server/fd.c b/server/fd.c
index c484b67..7a826f8 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -95,7 +95,6 @@
 #include "handle.h"
 #include "process.h"
 #include "request.h"
-#include "esync.h"
 
 #include "winternl.h"
 #include "winioctl.h"
@@ -160,7 +159,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 inproc_sync  *inproc_sync; /* in-process synchronization object */
 };
 
 static void fd_dump( struct object *obj, int verbose );
@@ -174,7 +173,6 @@ static const struct object_ops fd_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -187,6 +185,7 @@ static const struct object_ops fd_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     fd_destroy                /* destroy */
 };
@@ -216,7 +215,6 @@ static const struct object_ops device_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -229,6 +227,7 @@ static const struct object_ops device_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     device_destroy            /* destroy */
 };
@@ -257,7 +256,6 @@ static const struct object_ops inode_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -270,6 +268,7 @@ static const struct object_ops inode_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     inode_destroy             /* destroy */
 };
@@ -300,7 +299,6 @@ static const struct object_ops file_lock_ops =
     add_queue,                  /* add_queue */
     remove_queue,               /* remove_queue */
     file_lock_signaled,         /* signaled */
-    NULL,                       /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -313,6 +311,7 @@ static const struct object_ops file_lock_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
@@ -1662,9 +1661,7 @@ static void fd_destroy( struct object *obj )
         if (fd->unix_fd != -1) close( fd->unix_fd );
         free( fd->unix_name );
     }
-
-    if (do_esync())
-        close( fd->esync_fd );
+    if (fd->inproc_sync) release_object( fd->inproc_sync );
 }
 
 /* check if the desired access is possible without violating */
@@ -1783,16 +1780,13 @@ static struct fd *alloc_fd_object(void)
     fd->poll_index = -1;
     fd->completion = NULL;
     fd->comp_flags = 0;
-    fd->esync_fd   = -1;
+    fd->inproc_sync = NULL;
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
     init_async_queue( &fd->wait_q );
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
 
-    if (do_esync())
-        fd->esync_fd = esync_create_fd( 1, 0 );
-
     if ((fd->poll_index = add_poll_user( fd )) == -1)
     {
         release_object( fd );
@@ -1827,16 +1821,13 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
     fd->poll_index = -1;
     fd->completion = NULL;
     fd->comp_flags = 0;
+    fd->inproc_sync = NULL;
     fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
-    fd->esync_fd   = -1;
     init_async_queue( &fd->read_q );
     init_async_queue( &fd->write_q );
     init_async_queue( &fd->wait_q );
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
-
-    if (do_esync())
-        fd->esync_fd = esync_create_fd( 0, 0 );
     return fd;
 }
 
@@ -2311,10 +2302,15 @@ void set_fd_signaled( struct fd *fd, int signaled )
 {
     if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return;
     fd->signaled = signaled;
-    if (signaled) wake_up( fd->user, 0 );
-
-    if (do_esync() && !signaled)
-        esync_clear( fd->esync_fd );
+    if (signaled)
+    {
+        wake_up( fd->user, 0 );
+        set_inproc_event( fd->inproc_sync );
+    }
+    else
+    {
+        reset_inproc_event( fd->inproc_sync );
+    }
 }
 
 /* check if events are pending and if yes return which one(s) */
@@ -2340,12 +2336,16 @@ 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 inproc_sync *default_fd_get_inproc_sync( struct object *obj )
 {
     struct fd *fd = get_obj_fd( obj );
-    int ret = fd->esync_fd;
-    *type = ESYNC_MANUAL_SERVER;
+    struct inproc_sync *ret;
+
+    if (!fd->inproc_sync)
+        fd->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, fd->signaled );
+    ret = fd->inproc_sync;
     release_object( fd );
+    if (ret) grab_object( ret );
     return ret;
 }
 
diff --git a/server/file.c b/server/file.c
index 330d956..476408f 100644
--- a/server/file.c
+++ b/server/file.c
@@ -123,7 +123,6 @@ static const struct object_ops file_ops =
     add_queue,                    /* add_queue */
     remove_queue,                 /* remove_queue */
     default_fd_signaled,          /* signaled */
-    NULL,                         /* get_esync_fd */
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     file_get_fd,                  /* get_fd */
@@ -136,6 +135,7 @@ static const struct object_ops file_ops =
     NULL,                         /* unlink_name */
     file_open_file,               /* open_file */
     file_get_kernel_obj_list,     /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     file_destroy                  /* destroy */
 };
diff --git a/server/file.h b/server/file.h
index 815b0ad..bae0ec1 100644
--- a/server/file.h
+++ b/server/file.h
@@ -109,7 +109,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ) __WINE_DEALLOC(fre
 extern void get_nt_name( struct fd *fd, struct unicode_str *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 inproc_sync *default_fd_get_inproc_sync( struct object *obj );
 extern int default_fd_get_poll_events( struct fd *fd );
 extern void default_poll_event( struct fd *fd, int event );
 extern void fd_cancel_async( struct fd *fd, struct async *async );
diff --git a/server/handle.c b/server/handle.c
index 3d36af3..58b0fe2 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -126,7 +126,6 @@ static const struct object_ops handle_table_ops =
     no_add_queue,                    /* add_queue */
     NULL,                            /* remove_queue */
     NULL,                            /* signaled */
-    NULL,                            /* get_esync_fd */
     NULL,                            /* satisfied */
     no_signal,                       /* signal */
     no_get_fd,                       /* get_fd */
@@ -139,6 +138,7 @@ static const struct object_ops handle_table_ops =
     NULL,                            /* unlink_name */
     no_open_file,                    /* open_file */
     no_kernel_obj_list,              /* get_kernel_obj_list */
+    no_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                 /* close_handle */
     handle_table_destroy             /* destroy */
 };
diff --git a/server/hook.c b/server/hook.c
index 921aa8a..5faa63e 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -81,7 +81,6 @@ static const struct object_ops hook_table_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -94,6 +93,7 @@ static const struct object_ops hook_table_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     hook_table_destroy            /* destroy */
 };
diff --git a/server/inproc_sync.c b/server/inproc_sync.c
new file mode 100644
index 0000000..57d6bce
--- /dev/null
+++ b/server/inproc_sync.c
@@ -0,0 +1,418 @@
+/*
+ * In-process synchronization primitives
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "winternl.h"
+
+#include "file.h"
+#include "handle.h"
+#include "request.h"
+#include "thread.h"
+
+#ifdef HAVE_LINUX_NTSYNC_H
+# include <linux/ntsync.h>
+#endif
+
+#ifdef NTSYNC_IOC_EVENT_READ
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct linux_device
+{
+    struct object obj;      /* object header */
+    struct fd *fd;          /* fd for unix fd */
+};
+
+static struct object *linux_device_object;
+
+static void linux_device_dump( struct object *obj, int verbose );
+static struct fd *linux_device_get_fd( struct object *obj );
+static void linux_device_destroy( struct object *obj );
+static enum server_fd_type inproc_sync_get_fd_type( struct fd *fd );
+
+static const struct object_ops linux_device_ops =
+{
+    sizeof(struct linux_device),        /* size */
+    &no_type,                           /* type */
+    linux_device_dump,                  /* dump */
+    no_add_queue,                       /* add_queue */
+    NULL,                               /* remove_queue */
+    NULL,                               /* signaled */
+    NULL,                               /* satisfied */
+    no_signal,                          /* signal */
+    linux_device_get_fd,                /* get_fd */
+    default_map_access,                 /* map_access */
+    default_get_sd,                     /* get_sd */
+    default_set_sd,                     /* set_sd */
+    no_get_full_name,                   /* get_full_name */
+    no_lookup_name,                     /* lookup_name */
+    no_link_name,                       /* link_name */
+    NULL,                               /* unlink_name */
+    no_open_file,                       /* open_file */
+    no_kernel_obj_list,                 /* get_kernel_obj_list */
+    no_get_inproc_sync,                 /* get_inproc_sync */
+    no_close_handle,                    /* close_handle */
+    linux_device_destroy                /* destroy */
+};
+
+static const struct fd_ops inproc_sync_fd_ops =
+{
+    default_fd_get_poll_events,     /* get_poll_events */
+    default_poll_event,             /* poll_event */
+    inproc_sync_get_fd_type,        /* get_fd_type */
+    no_fd_read,                     /* read */
+    no_fd_write,                    /* write */
+    no_fd_flush,                    /* flush */
+    no_fd_get_file_info,            /* get_file_info */
+    no_fd_get_volume_info,          /* get_volume_info */
+    no_fd_ioctl,                    /* ioctl */
+    default_fd_cancel_async,        /* cancel_async */
+    no_fd_queue_async,              /* queue_async */
+    default_fd_reselect_async       /* reselect_async */
+};
+
+static void linux_device_dump( struct object *obj, int verbose )
+{
+    struct linux_device *device = (struct linux_device *)obj;
+    assert( obj->ops == &linux_device_ops );
+    fprintf( stderr, "In-process synchronization device fd=%p\n", device->fd );
+}
+
+static struct fd *linux_device_get_fd( struct object *obj )
+{
+    struct linux_device *device = (struct linux_device *)obj;
+    return (struct fd *)grab_object( device->fd );
+}
+
+static void linux_device_destroy( struct object *obj )
+{
+    struct linux_device *device = (struct linux_device *)obj;
+    assert( obj->ops == &linux_device_ops );
+    if (device->fd) release_object( device->fd );
+    linux_device_object = NULL;
+}
+
+static enum server_fd_type inproc_sync_get_fd_type( struct fd *fd )
+{
+    return FD_TYPE_FILE;
+}
+
+static struct linux_device *get_linux_device(void)
+{
+    struct linux_device *device;
+    int unix_fd;
+
+    if (linux_device_object)
+        return (struct linux_device *)grab_object( linux_device_object );
+
+    unix_fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY );
+    if (unix_fd == -1)
+    {
+        file_set_error();
+        return NULL;
+    }
+
+    if (!(device = alloc_object( &linux_device_ops )))
+    {
+        close( unix_fd );
+        return NULL;
+    }
+
+    if (!(device->fd = create_anonymous_fd( &inproc_sync_fd_ops, unix_fd, &device->obj, 0 )))
+    {
+        release_object( device );
+        return NULL;
+    }
+    allow_fd_caching( device->fd );
+
+    linux_device_object = grab_object( device );
+    make_object_permanent( linux_device_object );
+    return device;
+}
+
+struct inproc_sync
+{
+    struct object obj;
+    enum inproc_sync_type type;
+    struct fd *fd;
+};
+
+static void linux_obj_dump( struct object *obj, int verbose );
+static void linux_obj_destroy( struct object *obj );
+static struct fd *linux_obj_get_fd( struct object *obj );
+
+static const struct object_ops linux_obj_ops =
+{
+    sizeof(struct inproc_sync), /* size */
+    &no_type,                   /* type */
+    linux_obj_dump,             /* dump */
+    no_add_queue,               /* add_queue */
+    NULL,                       /* remove_queue */
+    NULL,                       /* signaled */
+    NULL,                       /* satisfied */
+    no_signal,                  /* signal */
+    linux_obj_get_fd,           /* get_fd */
+    default_map_access,         /* map_access */
+    default_get_sd,             /* get_sd */
+    default_set_sd,             /* set_sd */
+    no_get_full_name,           /* get_full_name */
+    no_lookup_name,             /* lookup_name */
+    no_link_name,               /* link_name */
+    NULL,                       /* unlink_name */
+    no_open_file,               /* open_file */
+    no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
+    no_close_handle,            /* close_handle */
+    linux_obj_destroy           /* destroy */
+};
+
+static void linux_obj_dump( struct object *obj, int verbose )
+{
+    struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+    assert( obj->ops == &linux_obj_ops );
+    fprintf( stderr, "In-process synchronization object type=%u fd=%p\n", inproc_sync->type, inproc_sync->fd );
+}
+
+static void linux_obj_destroy( struct object *obj )
+{
+    struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+    assert( obj->ops == &linux_obj_ops );
+    if (inproc_sync->fd) release_object( inproc_sync->fd );
+}
+
+static struct fd *linux_obj_get_fd( struct object *obj )
+{
+    struct inproc_sync *inproc_sync = (struct inproc_sync *)obj;
+    assert( obj->ops == &linux_obj_ops );
+    return (struct fd *)grab_object( inproc_sync->fd );
+}
+
+static struct inproc_sync *create_inproc_sync( enum inproc_sync_type type, int unix_fd )
+{
+    struct inproc_sync *inproc_sync;
+
+    if (!(inproc_sync = alloc_object( &linux_obj_ops )))
+    {
+        close( unix_fd );
+        return NULL;
+    }
+
+    inproc_sync->type = type;
+
+    if (!(inproc_sync->fd = create_anonymous_fd( &inproc_sync_fd_ops, unix_fd, &inproc_sync->obj, 0 )))
+    {
+        release_object( inproc_sync );
+        return NULL;
+    }
+    allow_fd_caching( inproc_sync->fd );
+
+    return inproc_sync;
+}
+
+struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled )
+{
+    struct ntsync_event_args args;
+    struct linux_device *device;
+    int event;
+
+    if (!(device = get_linux_device())) return NULL;
+
+    args.signaled = signaled;
+    switch (type)
+    {
+    case INPROC_SYNC_AUTO_EVENT:
+    case INPROC_SYNC_AUTO_SERVER:
+        args.manual = 0;
+        break;
+
+    case INPROC_SYNC_MANUAL_EVENT:
+    case INPROC_SYNC_MANUAL_SERVER:
+    case INPROC_SYNC_QUEUE:
+        args.manual = 1;
+        break;
+
+    default:
+        assert(0);
+        break;
+    }
+    if ((event = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_EVENT, &args )) < 0)
+    {
+        file_set_error();
+        release_object( device );
+        return NULL;
+    }
+    release_object( device );
+
+    return create_inproc_sync( type, event );
+}
+
+struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+    struct ntsync_sem_args args;
+    struct linux_device *device;
+    int semaphore;
+
+    if (!(device = get_linux_device())) return NULL;
+
+    args.count = count;
+    args.max = max;
+    if ((semaphore = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_SEM, &args )) < 0)
+    {
+        file_set_error();
+        release_object( device );
+        return NULL;
+    }
+
+    release_object( device );
+
+    return create_inproc_sync( INPROC_SYNC_SEMAPHORE, semaphore );
+}
+
+struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+    struct ntsync_mutex_args args;
+    struct linux_device *device;
+    int mutex;
+
+    if (!(device = get_linux_device())) return NULL;
+
+    args.owner = owner;
+    args.count = count;
+    if ((mutex = ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_MUTEX, &args )) < 0)
+    {
+        file_set_error();
+        release_object( device );
+        return NULL;
+    }
+
+    release_object( device );
+
+    return create_inproc_sync( INPROC_SYNC_MUTEX, mutex );
+}
+
+void set_inproc_event( struct inproc_sync *inproc_sync )
+{
+    __u32 count;
+
+    if (!inproc_sync) return;
+
+    if (debug_level) fprintf( stderr, "set_inproc_event %p\n", inproc_sync->fd );
+
+    ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_EVENT_SET, &count );
+}
+
+void reset_inproc_event( struct inproc_sync *inproc_sync )
+{
+    __u32 count;
+
+    if (!inproc_sync) return;
+
+    if (debug_level) fprintf( stderr, "set_inproc_event %p\n", inproc_sync->fd );
+
+    ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_EVENT_RESET, &count );
+}
+
+void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync )
+{
+    ioctl( get_unix_fd( inproc_sync->fd ), NTSYNC_IOC_MUTEX_KILL, &tid );
+}
+
+#else
+
+struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled )
+{
+    set_error( STATUS_NOT_IMPLEMENTED );
+    return NULL;
+}
+
+struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max )
+{
+    set_error( STATUS_NOT_IMPLEMENTED );
+    return NULL;
+}
+
+struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count )
+{
+    set_error( STATUS_NOT_IMPLEMENTED );
+    return NULL;
+}
+
+void set_inproc_event( struct inproc_sync *inproc_sync )
+{
+}
+
+void reset_inproc_event( struct inproc_sync *obj )
+{
+}
+
+void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync )
+{
+}
+
+#endif
+
+
+DECL_HANDLER(get_linux_sync_device)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+    struct linux_device *device;
+
+    if ((device = get_linux_device()))
+    {
+        reply->handle = alloc_handle_no_access_check( current->process, device, 0, 0 );
+        release_object( device );
+    }
+#else
+    set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
+
+DECL_HANDLER(get_linux_sync_obj)
+{
+#ifdef NTSYNC_IOC_EVENT_READ
+    struct object *obj;
+
+    if ((obj = get_handle_obj( current->process, req->handle, 0, NULL )))
+    {
+        struct inproc_sync *inproc_sync;
+
+        if ((inproc_sync = obj->ops->get_inproc_sync( obj )))
+        {
+            reply->handle = alloc_handle_no_access_check( current->process, inproc_sync, 0, 0 );
+            reply->type = inproc_sync->type;
+            reply->access = get_handle_access( current->process, req->handle );
+            release_object( inproc_sync );
+        }
+        release_object( obj );
+    }
+#else
+    set_error( STATUS_NOT_IMPLEMENTED );
+#endif
+}
diff --git a/server/mailslot.c b/server/mailslot.c
index e4c2445..929ca3e 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -81,7 +81,6 @@ static const struct object_ops mailslot_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     default_fd_signaled,       /* signaled */
-    NULL,                      /* get_esync_fd */
     no_satisfied,              /* satisfied */
     no_signal,                 /* signal */
     mailslot_get_fd,           /* get_fd */
@@ -94,6 +93,7 @@ static const struct object_ops mailslot_ops =
     default_unlink_name,       /* unlink_name */
     mailslot_open_file,        /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,/* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mailslot_destroy           /* destroy */
 };
@@ -143,7 +143,6 @@ static const struct object_ops mail_writer_ops =
     no_add_queue,               /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
-    NULL,                       /* get_esync_fd */
     NULL,                       /* satisfied */
     no_signal,                  /* signal */
     mail_writer_get_fd,         /* get_fd */
@@ -156,6 +155,7 @@ static const struct object_ops mail_writer_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    default_fd_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     mail_writer_destroy         /* destroy */
 };
@@ -209,7 +209,6 @@ static const struct object_ops mailslot_device_ops =
     no_add_queue,                   /* add_queue */
     NULL,                           /* remove_queue */
     NULL,                           /* signaled */
-    NULL,                           /* get_esync_fd */
     no_satisfied,                   /* satisfied */
     no_signal,                      /* signal */
     no_get_fd,                      /* get_fd */
@@ -222,6 +221,7 @@ static const struct object_ops mailslot_device_ops =
     default_unlink_name,            /* unlink_name */
     mailslot_device_open_file,      /* open_file */
     no_kernel_obj_list,             /* get_kernel_obj_list */
+    no_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                /* close_handle */
     mailslot_device_destroy         /* destroy */
 };
@@ -240,7 +240,6 @@ static const struct object_ops mailslot_device_file_ops =
     add_queue,                              /* add_queue */
     remove_queue,                           /* remove_queue */
     default_fd_signaled,                    /* signaled */
-    NULL,                                   /* get_esync_fd */
     no_satisfied,                           /* satisfied */
     no_signal,                              /* signal */
     mailslot_device_file_get_fd,            /* get_fd */
@@ -253,6 +252,7 @@ static const struct object_ops mailslot_device_file_ops =
     NULL,                                   /* unlink_name */
     no_open_file,                           /* open_file */
     no_kernel_obj_list,                     /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,             /* get_inproc_sync */
     no_close_handle,                        /* close_handle */
     mailslot_device_file_destroy            /* destroy */
 };
diff --git a/server/main.c b/server/main.c
index 052667b..e014ec5 100644
--- a/server/main.c
+++ b/server/main.c
@@ -34,7 +34,6 @@
 #include "thread.h"
 #include "request.h"
 #include "unicode.h"
-#include "esync.h"
 
 /* command-line options */
 int debug_level = 0;
@@ -230,9 +229,6 @@ int main( int argc, char *argv[] )
     sock_init();
     open_master_socket();
 
-    if (do_esync())
-        esync_init();
-
     if (debug_level) fprintf( stderr, "wineserver: starting (pid=%ld)\n", (long) getpid() );
     set_current_time();
     init_signals();
diff --git a/server/mapping.c b/server/mapping.c
index c6911f3..b0288c1 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -67,7 +67,6 @@ static const struct object_ops ranges_ops =
     no_add_queue,              /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    NULL,                      /* get_esync_fd */
     NULL,                      /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -80,6 +79,7 @@ static const struct object_ops ranges_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     ranges_destroy             /* destroy */
 };
@@ -104,7 +104,6 @@ static const struct object_ops shared_map_ops =
     no_add_queue,              /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    NULL,                      /* get_esync_fd */
     NULL,                      /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -117,6 +116,7 @@ static const struct object_ops shared_map_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     shared_map_destroy         /* destroy */
 };
@@ -178,7 +178,6 @@ static const struct object_ops mapping_ops =
     no_add_queue,                /* add_queue */
     NULL,                        /* remove_queue */
     NULL,                        /* signaled */
-    NULL,                        /* get_esync_fd */
     NULL,                        /* satisfied */
     no_signal,                   /* signal */
     mapping_get_fd,              /* get_fd */
@@ -191,6 +190,7 @@ static const struct object_ops mapping_ops =
     default_unlink_name,         /* unlink_name */
     no_open_file,                /* open_file */
     no_kernel_obj_list,          /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     mapping_destroy              /* destroy */
 };
diff --git a/server/mutex.c b/server/mutex.c
index 4785a83..e71b95c 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -38,6 +38,8 @@
 
 static const WCHAR mutex_name[] = {'M','u','t','a','n','t'};
 
+static struct list inproc_mutexes = LIST_INIT(inproc_mutexes);
+
 struct type_descr mutex_type =
 {
     { mutex_name, sizeof(mutex_name) },   /* name */
@@ -57,6 +59,8 @@ struct mutex
     unsigned int   count;           /* recursion count */
     int            abandoned;       /* has it been abandoned? */
     struct list    entry;           /* entry in owner thread mutex list */
+    struct list    inproc_mutexes_entry; /* entry in inproc_mutexes list */
+    struct inproc_sync *inproc_sync;/* in-process synchronization object */
 };
 
 static void mutex_dump( struct object *obj, int verbose );
@@ -64,6 +68,7 @@ static int mutex_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void mutex_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static void mutex_destroy( struct object *obj );
 static int mutex_signal( struct object *obj, unsigned int access );
+static struct inproc_sync *mutex_get_inproc_sync( struct object *obj );
 
 static const struct object_ops mutex_ops =
 {
@@ -73,7 +78,6 @@ static const struct object_ops mutex_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     mutex_signaled,            /* signaled */
-    NULL,                      /* get_esync_fd */
     mutex_satisfied,           /* satisfied */
     mutex_signal,              /* signal */
     no_get_fd,                 /* get_fd */
@@ -86,6 +90,7 @@ static const struct object_ops mutex_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    mutex_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     mutex_destroy              /* destroy */
 };
@@ -128,6 +133,7 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
             mutex->owner = NULL;
             mutex->abandoned = 0;
             if (owned) do_grab( mutex, current );
+            mutex->inproc_sync = NULL;
         }
     }
     return mutex;
@@ -135,16 +141,22 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str
 
 void abandon_mutexes( struct thread *thread )
 {
+    struct mutex *mutex;
     struct list *ptr;
 
     while ((ptr = list_head( &thread->mutex_list )) != NULL)
     {
-        struct mutex *mutex = LIST_ENTRY( ptr, struct mutex, entry );
+        mutex = LIST_ENTRY( ptr, struct mutex, entry );
         assert( mutex->owner == thread );
         mutex->count = 0;
         mutex->abandoned = 1;
         do_release( mutex );
     }
+
+    LIST_FOR_EACH_ENTRY(mutex, &inproc_mutexes, struct mutex, inproc_mutexes_entry)
+    {
+        abandon_inproc_mutex( thread->id, mutex->inproc_sync );
+    }
 }
 
 static void mutex_dump( struct object *obj, int verbose )
@@ -190,14 +202,38 @@ static int mutex_signal( struct object *obj, unsigned int access )
     return 1;
 }
 
+static struct inproc_sync *mutex_get_inproc_sync( struct object *obj )
+{
+    struct mutex *mutex = (struct mutex *)obj;
+
+    /* This state will always be the state that the mutex was created with.
+     * We could create the inproc_sync at creation time to make this clearer,
+     * but some broken programs create hundreds of thousands of handles which
+     * they never use, and we want to avoid wasting memory or fds in that case. */
+    if (!mutex->inproc_sync)
+    {
+        mutex->inproc_sync = create_inproc_mutex( mutex->owner ? mutex->owner->id : 0, mutex->count );
+        if (mutex->inproc_sync) list_add_tail( &inproc_mutexes, &mutex->inproc_mutexes_entry );
+    }
+    if (mutex->inproc_sync) grab_object( mutex->inproc_sync );
+    return mutex->inproc_sync;
+}
+
 static void mutex_destroy( struct object *obj )
 {
     struct mutex *mutex = (struct mutex *)obj;
     assert( obj->ops == &mutex_ops );
 
-    if (!mutex->count) return;
-    mutex->count = 0;
-    do_release( mutex );
+    if (mutex->count)
+    {
+        mutex->count = 0;
+        do_release( mutex );
+    }
+    if (mutex->inproc_sync)
+    {
+        release_object( mutex->inproc_sync );
+        list_remove( &mutex->inproc_mutexes_entry );
+    }
 }
 
 /* create a mutex */
diff --git a/server/named_pipe.c b/server/named_pipe.c
index fa74ff8..2ce2756 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -119,7 +119,6 @@ static const struct object_ops named_pipe_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -132,6 +131,7 @@ static const struct object_ops named_pipe_ops =
     default_unlink_name,          /* unlink_name */
     named_pipe_open_file,         /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     named_pipe_destroy            /* destroy */
 };
@@ -168,7 +168,6 @@ static const struct object_ops pipe_server_ops =
     add_queue,                    /* add_queue */
     remove_queue,                 /* remove_queue */
     default_fd_signaled,          /* signaled */
-    default_fd_get_esync_fd,      /* get_esync_fd */
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     pipe_end_get_fd,              /* get_fd */
@@ -181,6 +180,7 @@ static const struct object_ops pipe_server_ops =
     NULL,                         /* unlink_name */
     pipe_server_open_file,        /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_server_destroy           /* destroy */
 };
@@ -213,7 +213,6 @@ static const struct object_ops pipe_client_ops =
     add_queue,                    /* add_queue */
     remove_queue,                 /* remove_queue */
     default_fd_signaled,          /* signaled */
-    default_fd_get_esync_fd,      /* get_esync_fd */
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     pipe_end_get_fd,              /* get_fd */
@@ -226,6 +225,7 @@ static const struct object_ops pipe_client_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     async_close_obj_handle,       /* close_handle */
     pipe_end_destroy              /* destroy */
 };
@@ -261,7 +261,6 @@ static const struct object_ops named_pipe_device_ops =
     no_add_queue,                     /* add_queue */
     NULL,                             /* remove_queue */
     NULL,                             /* signaled */
-    NULL,                             /* get_esync_fd */
     no_satisfied,                     /* satisfied */
     no_signal,                        /* signal */
     no_get_fd,                        /* get_fd */
@@ -274,6 +273,7 @@ static const struct object_ops named_pipe_device_ops =
     default_unlink_name,              /* unlink_name */
     named_pipe_device_open_file,      /* open_file */
     no_kernel_obj_list,               /* get_kernel_obj_list */
+    no_get_inproc_sync,               /* get_inproc_sync */
     no_close_handle,                  /* close_handle */
     named_pipe_device_destroy         /* destroy */
 };
@@ -293,7 +293,6 @@ static const struct object_ops named_pipe_device_file_ops =
     add_queue,                               /* add_queue */
     remove_queue,                            /* remove_queue */
     default_fd_signaled,                     /* signaled */
-    NULL,                                    /* get_esync_fd */
     no_satisfied,                            /* satisfied */
     no_signal,                               /* signal */
     named_pipe_device_file_get_fd,           /* get_fd */
@@ -306,6 +305,7 @@ static const struct object_ops named_pipe_device_file_ops =
     NULL,                                    /* unlink_name */
     no_open_file,                            /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_device_file_destroy           /* destroy */
 };
@@ -344,7 +344,6 @@ static const struct object_ops named_pipe_dir_ops =
     add_queue,                               /* add_queue */
     remove_queue,                            /* remove_queue */
     default_fd_signaled,                     /* signaled */
-    NULL,                                    /* get_esync_fd */
     no_satisfied,                            /* satisfied */
     no_signal,                               /* signal */
     named_pipe_dir_get_fd,                   /* get_fd */
@@ -357,6 +356,7 @@ static const struct object_ops named_pipe_dir_ops =
     NULL,                                    /* unlink_name */
     named_pipe_dir_open_file,                /* open_file */
     no_kernel_obj_list,                      /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,              /* get_inproc_sync */
     no_close_handle,                         /* close_handle */
     named_pipe_dir_destroy                   /* destroy */
 };
diff --git a/server/object.c b/server/object.c
index 3aeb6bd..2faabe9 100644
--- a/server/object.c
+++ b/server/object.c
@@ -108,7 +108,6 @@ static const struct object_ops apc_reserve_ops =
     no_add_queue,               /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
-    NULL,                       /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -121,6 +120,7 @@ static const struct object_ops apc_reserve_ops =
     default_unlink_name,        /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
@@ -133,7 +133,6 @@ static const struct object_ops completion_reserve_ops =
     no_add_queue,              /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    NULL,                      /* get_esync_fd */
     no_satisfied,              /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -146,6 +145,7 @@ static const struct object_ops completion_reserve_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     no_destroy                 /* destroy */
 };
@@ -641,6 +641,12 @@ struct fd *no_get_fd( struct object *obj )
     return NULL;
 }
 
+struct inproc_sync *no_get_inproc_sync( struct object *obj )
+{
+    set_error( STATUS_OBJECT_TYPE_MISMATCH );
+    return NULL;
+}
+
 unsigned int default_map_access( struct object *obj, unsigned int access )
 {
     return map_access( access, &obj->ops->type->mapping );
diff --git a/server/object.h b/server/object.h
index 7968dbe..0caac02 100644
--- a/server/object.h
+++ b/server/object.h
@@ -42,6 +42,7 @@ struct async;
 struct async_queue;
 struct winstation;
 struct object_type;
+struct inproc_sync;
 
 
 struct unicode_str
@@ -78,8 +79,6 @@ struct object_ops
     void (*remove_queue)(struct object *,struct wait_queue_entry *);
     /* 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);
     /* wait satisfied */
     void (*satisfied)(struct object *,struct wait_queue_entry *);
     /* signal an object */
@@ -105,6 +104,8 @@ struct object_ops
                                 unsigned int options);
     /* return list of kernel objects */
     struct list *(*get_kernel_obj_list)(struct object *);
+    /* get a client-waitable in-process synchronization handle to this object */
+    struct inproc_sync *(*get_inproc_sync)(struct object *);
     /* close a handle to this object */
     int (*close_handle)(struct object *,struct process *,obj_handle_t);
     /* destroy on refcount == 0 */
@@ -233,6 +234,17 @@ extern void reset_event( struct event *event );
 
 extern void abandon_mutexes( struct thread *thread );
 
+/* in-process synchronization functions */
+
+extern struct inproc_sync *create_inproc_event( enum inproc_sync_type type, int signaled );
+extern struct inproc_sync *create_inproc_mutex( thread_id_t owner, unsigned int count );
+extern struct inproc_sync *create_inproc_semaphore( unsigned int count, unsigned int max );
+extern void set_inproc_event( struct inproc_sync *obj );
+extern void reset_inproc_event( struct inproc_sync *obj );
+extern void abandon_inproc_mutex( thread_id_t tid, struct inproc_sync *inproc_sync );
+
+extern struct inproc_sync *no_get_inproc_sync( struct object *obj );
+
 /* serial functions */
 
 int get_serial_async_timeout(struct object *obj, int type, int count);
diff --git a/server/process.c b/server/process.c
index 885f5af..4420200 100644
--- a/server/process.c
+++ b/server/process.c
@@ -63,7 +63,6 @@
 #include "request.h"
 #include "user.h"
 #include "security.h"
-#include "esync.h"
 
 /* process object */
 
@@ -95,8 +94,8 @@ static unsigned int process_map_access( struct object *obj, unsigned int access
 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 struct inproc_sync *process_get_inproc_sync( struct object *obj );
 static void process_destroy( struct object *obj );
-static int 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 =
@@ -107,7 +106,6 @@ static const struct object_ops process_ops =
     add_queue,                   /* add_queue */
     remove_queue,                /* remove_queue */
     process_signaled,            /* signaled */
-    process_get_esync_fd,        /* get_esync_fd */
     no_satisfied,                /* satisfied */
     no_signal,                   /* signal */
     no_get_fd,                   /* get_fd */
@@ -120,6 +118,7 @@ static const struct object_ops process_ops =
     NULL,                        /* unlink_name */
     no_open_file,                /* open_file */
     process_get_kernel_obj_list, /* get_kernel_obj_list */
+    process_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,             /* close_handle */
     process_destroy              /* destroy */
 };
@@ -159,7 +158,6 @@ static const struct object_ops startup_info_ops =
     add_queue,                     /* add_queue */
     remove_queue,                  /* remove_queue */
     startup_info_signaled,         /* signaled */
-    NULL,                          /* get_esync_fd */
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
@@ -172,6 +170,7 @@ static const struct object_ops startup_info_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     startup_info_destroy           /* destroy */
 };
@@ -194,6 +193,7 @@ struct type_descr job_type =
 
 static void job_dump( struct object *obj, int verbose );
 static int job_signaled( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *job_get_inproc_sync( struct object *obj );
 static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
 static void job_destroy( struct object *obj );
 
@@ -211,6 +211,7 @@ struct job
     struct job *parent;
     struct list parent_job_entry;  /* list entry for parent job */
     struct list child_job_list;    /* list of child jobs */
+    struct inproc_sync *inproc_sync; /* in-process synchronization object */
 };
 
 static const struct object_ops job_ops =
@@ -221,7 +222,6 @@ static const struct object_ops job_ops =
     add_queue,                     /* add_queue */
     remove_queue,                  /* remove_queue */
     job_signaled,                  /* signaled */
-    NULL,                          /* get_esync_fd */
     no_satisfied,                  /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
@@ -234,6 +234,7 @@ static const struct object_ops job_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    job_get_inproc_sync,           /* get_inproc_sync */
     job_close_handle,              /* close_handle */
     job_destroy                    /* destroy */
 };
@@ -258,6 +259,7 @@ static struct job *create_job_object( struct object *root, const struct unicode_
             job->completion_port = NULL;
             job->completion_key = 0;
             job->parent = NULL;
+            job->inproc_sync = NULL;
         }
     }
     return job;
@@ -414,6 +416,17 @@ static void terminate_job( struct job *job, int exit_code )
     job->terminating = 0;
     job->signaled = 1;
     wake_up( &job->obj, 0 );
+    set_inproc_event( job->inproc_sync );
+}
+
+static struct inproc_sync *job_get_inproc_sync( struct object *obj )
+{
+    struct job *job = (struct job *)obj;
+
+    if (!job->inproc_sync)
+        job->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, job->signaled );
+    if (job->inproc_sync) grab_object( job->inproc_sync );
+    return job->inproc_sync;
 }
 
 static int job_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
@@ -444,6 +457,8 @@ static void job_destroy( struct object *obj )
         list_remove( &job->parent_job_entry );
         release_object( job->parent );
     }
+
+    if (job->inproc_sync) release_object( job->inproc_sync );
 }
 
 static void job_dump( struct object *obj, int verbose )
@@ -689,9 +704,9 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla
     process->rawinput_device_count = 0;
     process->rawinput_mouse  = NULL;
     process->rawinput_kbd    = NULL;
+    process->inproc_sync     = NULL;
     memset( &process->image_info, 0, sizeof(process->image_info) );
     list_init( &process->rawinput_entry );
-    process->esync_fd        = -1;
     list_init( &process->kernel_object );
     list_init( &process->thread_list );
     list_init( &process->locks );
@@ -742,9 +757,6 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla
     if (!process->handles || !process->token) goto error;
     process->session_id = token_get_session_id( process->token );
 
-    if (do_esync())
-        process->esync_fd = esync_create_fd( 0, 0 );
-
     set_fd_events( process->msg_fd, POLLIN );  /* start listening to events */
     return process;
 
@@ -793,7 +805,8 @@ static void process_destroy( struct object *obj )
     free( process->rawinput_devices );
     free( process->dir_cache );
     free( process->image );
-    if (do_esync()) close( process->esync_fd );
+
+    if (process->inproc_sync) release_object( process->inproc_sync );
 }
 
 /* dump a process on stdout for debugging purposes */
@@ -811,13 +824,6 @@ 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 )
-{
-    struct process *process = (struct process *)obj;
-    *type = ESYNC_MANUAL_SERVER;
-    return process->esync_fd;
-}
-
 static unsigned int process_map_access( struct object *obj, unsigned int access )
 {
     access = default_map_access( obj, access );
@@ -834,6 +840,16 @@ static struct list *process_get_kernel_obj_list( struct object *obj )
     return &process->kernel_object;
 }
 
+static struct inproc_sync *process_get_inproc_sync( struct object *obj )
+{
+    struct process *process = (struct process *)obj;
+
+    if (!process->inproc_sync)
+        process->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, !process->running_threads );
+    if (process->inproc_sync) grab_object( process->inproc_sync );
+    return process->inproc_sync;
+}
+
 static struct security_descriptor *process_get_sd( struct object *obj )
 {
     static struct security_descriptor *process_default_sd;
@@ -998,6 +1014,7 @@ static void process_killed( struct process *process )
     release_job_process( process );
     start_sigkill_timer( process );
     wake_up( &process->obj, 0 );
+    set_inproc_event( process->inproc_sync );
 }
 
 /* add a thread to a process running threads list */
diff --git a/server/process.h b/server/process.h
index a586ddb..681b75e 100644
--- a/server/process.h
+++ b/server/process.h
@@ -86,7 +86,7 @@ struct process
     struct list          rawinput_entry;  /* entry in the rawinput process list */
     struct list          kernel_object;   /* list of kernel object pointers */
     struct pe_image_info image_info;      /* main exe image info */
-    int                  esync_fd;        /* esync file descriptor (signaled on exit) */
+    struct inproc_sync  *inproc_sync;     /* in-process synchronization object */
 };
 
 /* process functions */
diff --git a/server/protocol.def b/server/protocol.def
index 80c56d5..eb165d9 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4088,6 +4088,7 @@ struct handle_info
     obj_handle_t handle;       /* next process handle */
 @END
 
+
 /* Iterate thread list for process */
 @REQ(get_next_thread)
     obj_handle_t process;      /* process handle */
@@ -4099,52 +4100,44 @@ struct handle_info
     obj_handle_t handle;       /* next thread handle */
 @END
 
-enum esync_type
+enum inproc_sync_type
 {
-    ESYNC_SEMAPHORE = 1,
-    ESYNC_AUTO_EVENT,
-    ESYNC_MANUAL_EVENT,
-    ESYNC_MUTEX,
-    ESYNC_AUTO_SERVER,
-    ESYNC_MANUAL_SERVER,
-    ESYNC_QUEUE,
+    INPROC_SYNC_UNKNOWN,
+    INPROC_SYNC_AUTO_EVENT,
+    INPROC_SYNC_MANUAL_EVENT,
+    INPROC_SYNC_SEMAPHORE,
+    INPROC_SYNC_MUTEX,
+    INPROC_SYNC_AUTO_SERVER,
+    INPROC_SYNC_MANUAL_SERVER,
+    INPROC_SYNC_QUEUE,
 };
 
-/* Create a new eventfd-based synchronization object */
-@REQ(create_esync)
-    unsigned int access;        /* wanted access rights */
-    int          initval;       /* initial value */
-    int          type;          /* type of esync object */
-    int          max;           /* maximum count on a semaphore */
-    VARARG(objattr,object_attributes); /* object attributes */
+
+/* Obtain a handle to the fast synchronization device object */
+@REQ(get_linux_sync_device)
 @REPLY
-    obj_handle_t handle;        /* handle to the object */
-    int          type;          /* actual type (may be different for events) */
-    unsigned int shm_idx;
+    obj_handle_t handle;          /* handle to the device */
 @END
 
-@REQ(open_esync)
-    unsigned int access;        /* wanted access rights */
-    unsigned int attributes;    /* object attributes */
-    obj_handle_t rootdir;       /* root directory */
-    int          type;          /* type of esync object (above) */
-    VARARG(name,unicode_str);   /* object name */
+
+/* Get the in-process synchronization object associated with the given handle */
+@REQ(get_linux_sync_obj)
+    obj_handle_t handle;          /* handle to the object */
 @REPLY
-    obj_handle_t handle;        /* handle to the event */
-    int          type;          /* type of esync object (above) */
-    unsigned int shm_idx;       /* this object's index into the shm section */
+    obj_handle_t handle;          /* handle to the fast synchronization object */
+    int          type;            /* object type */
+    unsigned int access;          /* handle access rights */
 @END
 
-/* Retrieve the esync fd for an object. */
-@REQ(get_esync_fd)
-    obj_handle_t handle;        /* handle to the object */
-@REPLY
-    int          type;
-    unsigned int shm_idx;
+
+/* Begin a client-side wait on a message queue */
+@REQ(select_inproc_queue)
 @END
 
-@REQ(esync_msgwait)
-    int          in_msgwait;    /* are we in a message wait? */
+
+/* End a client-side wait on a message queue */
+@REQ(unselect_inproc_queue)
+    int          signaled;        /* was the queue signaled? */
 @END
 
 /* Setup keyboard auto-repeat */
@@ -4156,6 +4149,9 @@ enum esync_type
     int enable;                /* previous state of auto-repeat enable */
 @END
 
-/* Retrieve the fd to wait on for user APCs. */
-@REQ(get_esync_apc_fd)
+
+/* Get an event handle to be used for thread alerts with in-process synchronization */
+@REQ(get_inproc_alert_event)
+@REPLY
+    obj_handle_t handle;          /* handle to the event */
 @END
diff --git a/server/queue.c b/server/queue.c
index 985c508..461fc80 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -44,7 +44,6 @@
 #include "process.h"
 #include "request.h"
 #include "user.h"
-#include "esync.h"
 
 #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
 #define WM_NCMOUSELAST  (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST))
@@ -135,8 +134,8 @@ struct msg_queue
     int                    keystate_lock;   /* owns an input keystate lock */
     const queue_shm_t     *shared;          /* queue in session shared memory */
     unsigned int           ignore_post_msg; /* ignore post messages newer than this unique id */
-    int                    esync_fd;        /* esync file descriptor (signalled on message) */
-    int                    esync_in_msgwait; /* our thread is currently waiting on us */
+    struct inproc_sync    *inproc_sync;     /* in-process synchronization object */
+    int                    in_inproc_wait;  /* are we in a client-side wait? */
 };
 
 struct hotkey
@@ -153,8 +152,8 @@ 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 void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *msg_queue_get_inproc_sync( struct object *obj );
 static void msg_queue_destroy( struct object *obj );
 static void msg_queue_poll_event( struct fd *fd, int event );
 static void thread_input_dump( struct object *obj, int verbose );
@@ -169,7 +168,6 @@ static const struct object_ops msg_queue_ops =
     msg_queue_add_queue,       /* add_queue */
     msg_queue_remove_queue,    /* remove_queue */
     msg_queue_signaled,        /* signaled */
-    msg_queue_get_esync_fd,    /* get_esync_fd */
     msg_queue_satisfied,       /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -182,6 +180,7 @@ static const struct object_ops msg_queue_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    msg_queue_get_inproc_sync, /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     msg_queue_destroy          /* destroy */
 };
@@ -207,7 +206,6 @@ static const struct object_ops thread_input_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -220,6 +218,7 @@ static const struct object_ops thread_input_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     thread_input_destroy          /* destroy */
 };
@@ -320,8 +319,8 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         queue->last_get_msg    = current_time;
         queue->keystate_lock   = 0;
         queue->ignore_post_msg = 0;
-        queue->esync_fd        = -1;
-        queue->esync_in_msgwait = 0;
+        queue->inproc_sync     = NULL;
+        queue->in_inproc_wait  = 0;
         list_init( &queue->send_result );
         list_init( &queue->callback_result );
         list_init( &queue->pending_timers );
@@ -344,9 +343,6 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         }
         SHARED_WRITE_END;
 
-        if (do_esync())
-            queue->esync_fd = esync_create_fd( 0, 0 );
-
         thread->queue = queue;
 
         if ((desktop = get_thread_desktop( thread, 0 )))
@@ -741,7 +737,11 @@ static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits )
     }
     SHARED_WRITE_END;
 
-    if (is_signaled( queue )) wake_up( &queue->obj, 0 );
+    if (is_signaled( queue ))
+    {
+        wake_up( &queue->obj, 0 );
+        set_inproc_event( queue->inproc_sync );
+    }
 }
 
 /* clear some queue bits */
@@ -761,9 +761,8 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits
         if (queue->keystate_lock) unlock_input_keystate( queue->input );
         queue->keystate_lock = 0;
     }
-
-    if (do_esync() && !is_signaled( queue ))
-        esync_clear( queue->esync_fd );
+    if (!is_signaled( queue ))
+        reset_inproc_event( queue->inproc_sync );
 }
 
 /* check if message is matched by the filter */
@@ -1288,8 +1287,8 @@ static int is_queue_hung( struct msg_queue *queue )
             return 0;  /* thread is waiting on queue -> not hung */
     }
 
-    if (do_esync() && queue->esync_in_msgwait)
-        return 0;   /* thread is waiting on queue in absentia -> not hung */
+    if (queue->in_inproc_wait)
+        return 0;  /* thread is waiting on queue in absentia -> not hung */
 
     return 1;
 }
@@ -1345,13 +1344,6 @@ 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 )
-{
-    struct msg_queue *queue = (struct msg_queue *)obj;
-    *type = ESYNC_QUEUE;
-    return queue->esync_fd;
-}
-
 static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct msg_queue *queue = (struct msg_queue *)obj;
@@ -1363,6 +1355,18 @@ static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *en
         shared->changed_mask = 0;
     }
     SHARED_WRITE_END;
+
+    reset_inproc_event( queue->inproc_sync );
+}
+
+static struct inproc_sync *msg_queue_get_inproc_sync( struct object *obj )
+{
+    struct msg_queue *queue = (struct msg_queue *)obj;
+
+    if (!queue->inproc_sync)
+        queue->inproc_sync = create_inproc_event( INPROC_SYNC_QUEUE, is_signaled( queue ) );
+    if (queue->inproc_sync) grab_object( queue->inproc_sync );
+    return queue->inproc_sync;
 }
 
 static void msg_queue_destroy( struct object *obj )
@@ -1408,7 +1412,7 @@ static void msg_queue_destroy( struct object *obj )
     if (queue->hooks) release_object( queue->hooks );
     if (queue->fd) release_object( queue->fd );
     if (queue->shared) free_shared_object( queue->shared );
-    if (do_esync()) close( queue->esync_fd );
+    if (queue->inproc_sync) release_object( queue->inproc_sync );
 }
 
 static void msg_queue_poll_event( struct fd *fd, int event )
@@ -1419,6 +1423,7 @@ static void msg_queue_poll_event( struct fd *fd, int event )
     if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
     else set_fd_events( queue->fd, 0 );
     wake_up( &queue->obj, 0 );
+    set_inproc_event( queue->inproc_sync );
 }
 
 static void thread_input_dump( struct object *obj, int verbose )
@@ -3203,11 +3208,16 @@ DECL_HANDLER(set_queue_mask)
                 }
                 SHARED_WRITE_END;
             }
-            else wake_up( &queue->obj, 0 );
+            else
+            {
+                wake_up( &queue->obj, 0 );
+                set_inproc_event( queue->inproc_sync );
+            }
+        }
+        else
+        {
+            reset_inproc_event( queue->inproc_sync );
         }
-
-        if (do_esync() && !is_signaled( queue ))
-            esync_clear( queue->esync_fd );
     }
 }
 
@@ -3229,8 +3239,8 @@ DECL_HANDLER(get_queue_status)
         }
         SHARED_WRITE_END;
 
-        if (do_esync() && !is_signaled( queue ))
-            esync_clear( queue->esync_fd );
+        if (!is_signaled( queue ))
+            reset_inproc_event( queue->inproc_sync );
     }
     else reply->wake_bits = reply->changed_bits = 0;
 }
@@ -3429,6 +3439,9 @@ DECL_HANDLER(get_message)
     }
     SHARED_WRITE_END;
 
+    if (!is_signaled( queue ))
+        reset_inproc_event( queue->inproc_sync );
+
     /* then check for posted messages */
     if ((filter & QS_POSTMESSAGE) &&
         get_posted_message( queue, queue->ignore_post_msg, get_win, req->get_first, req->get_last, req->flags, reply ))
@@ -3498,11 +3511,8 @@ DECL_HANDLER(get_message)
     }
     SHARED_WRITE_END;
 
+    reset_inproc_event( queue->inproc_sync );
     set_error( STATUS_PENDING );  /* FIXME */
-
-    if (do_esync() && !is_signaled( queue ))
-        esync_clear( queue->esync_fd );
-
     return;
 
 found_msg:
@@ -4287,23 +4297,6 @@ DECL_HANDLER(update_rawinput_devices)
     }
 }
 
-DECL_HANDLER(esync_msgwait)
-{
-    struct msg_queue *queue = get_current_queue();
-    const queue_shm_t *queue_shm;
-
-    if (!queue) return;
-    queue_shm = queue->shared;
-    queue->esync_in_msgwait = req->in_msgwait;
-
-    if (current->process->idle_event && !(queue_shm->wake_mask & QS_SMRESULT))
-        set_event( current->process->idle_event );
-
-    /* and start/stop waiting on the driver */
-    if (queue->fd)
-        set_fd_events( queue->fd, req->in_msgwait ? POLLIN : 0 );
-}
-
 DECL_HANDLER(set_keyboard_repeat)
 {
     struct desktop *desktop;
@@ -4323,3 +4316,41 @@ DECL_HANDLER(set_keyboard_repeat)
     release_object( desktop );
 }
 
+DECL_HANDLER(select_inproc_queue)
+{
+    struct msg_queue *queue = current->queue;
+
+    if (queue->in_inproc_wait)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+    }
+    else
+    {
+        check_thread_queue_idle( current );
+
+        if (queue->fd)
+            set_fd_events( queue->fd, POLLIN );
+
+        queue->in_inproc_wait = 1;
+    }
+}
+
+DECL_HANDLER(unselect_inproc_queue)
+{
+    struct msg_queue *queue = current->queue;
+
+    if (!queue->in_inproc_wait)
+    {
+        set_error( STATUS_ACCESS_DENIED );
+    }
+    else
+    {
+        if (queue->fd)
+            set_fd_events( queue->fd, 0 );
+
+        if (req->signaled)
+            msg_queue_satisfied( &queue->obj, NULL );
+
+        queue->in_inproc_wait = 0;
+    }
+}
diff --git a/server/registry.c b/server/registry.c
index bfd7818..4b19e98 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -180,7 +180,6 @@ static const struct object_ops key_ops =
     no_add_queue,            /* add_queue */
     NULL,                    /* remove_queue */
     NULL,                    /* signaled */
-    NULL,                    /* get_esync_fd */
     NULL,                    /* satisfied */
     no_signal,               /* signal */
     no_get_fd,               /* get_fd */
@@ -193,6 +192,7 @@ static const struct object_ops key_ops =
     key_unlink_name,         /* unlink_name */
     no_open_file,            /* open_file */
     no_kernel_obj_list,      /* get_kernel_obj_list */
+    no_get_inproc_sync,      /* get_inproc_sync */
     key_close_handle,        /* close_handle */
     key_destroy              /* destroy */
 };
diff --git a/server/request.c b/server/request.c
index f4f5e71..24a4f77 100644
--- a/server/request.c
+++ b/server/request.c
@@ -89,7 +89,6 @@ static const struct object_ops master_socket_ops =
     no_add_queue,                  /* add_queue */
     NULL,                          /* remove_queue */
     NULL,                          /* signaled */
-    NULL,                          /* get_esync_fd */
     NULL,                          /* satisfied */
     no_signal,                     /* signal */
     no_get_fd,                     /* get_fd */
@@ -102,6 +101,7 @@ static const struct object_ops master_socket_ops =
     NULL,                          /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    no_get_inproc_sync,            /* get_inproc_sync */
     no_close_handle,               /* close_handle */
     master_socket_destroy          /* destroy */
 };
diff --git a/server/request_handlers.h b/server/request_handlers.h
index 71c83b6..8dff83d 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -300,12 +300,12 @@ DECL_HANDLER(suspend_process);
 DECL_HANDLER(resume_process);
 DECL_HANDLER(get_next_process);
 DECL_HANDLER(get_next_thread);
-DECL_HANDLER(create_esync);
-DECL_HANDLER(open_esync);
-DECL_HANDLER(get_esync_fd);
-DECL_HANDLER(esync_msgwait);
+DECL_HANDLER(get_linux_sync_device);
+DECL_HANDLER(get_linux_sync_obj);
+DECL_HANDLER(select_inproc_queue);
+DECL_HANDLER(unselect_inproc_queue);
 DECL_HANDLER(set_keyboard_repeat);
-DECL_HANDLER(get_esync_apc_fd);
+DECL_HANDLER(get_inproc_alert_event);
 
 typedef void (*req_handler)( const void *req, void *reply );
 static const req_handler req_handlers[REQ_NB_REQUESTS] =
@@ -603,12 +603,12 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_resume_process,
     (req_handler)req_get_next_process,
     (req_handler)req_get_next_thread,
-    (req_handler)req_create_esync,
-    (req_handler)req_open_esync,
-    (req_handler)req_get_esync_fd,
-    (req_handler)req_esync_msgwait,
+    (req_handler)req_get_linux_sync_device,
+    (req_handler)req_get_linux_sync_obj,
+    (req_handler)req_select_inproc_queue,
+    (req_handler)req_unselect_inproc_queue,
     (req_handler)req_set_keyboard_repeat,
-    (req_handler)req_get_esync_apc_fd,
+    (req_handler)req_get_inproc_alert_event,
 };
 
 C_ASSERT( sizeof(abstime_t) == 8 );
@@ -2293,35 +2293,24 @@ C_ASSERT( offsetof(struct get_next_thread_request, flags) == 28 );
 C_ASSERT( sizeof(struct get_next_thread_request) == 32 );
 C_ASSERT( offsetof(struct get_next_thread_reply, handle) == 8 );
 C_ASSERT( sizeof(struct get_next_thread_reply) == 16 );
-C_ASSERT( offsetof(struct create_esync_request, access) == 12 );
-C_ASSERT( offsetof(struct create_esync_request, initval) == 16 );
-C_ASSERT( offsetof(struct create_esync_request, type) == 20 );
-C_ASSERT( offsetof(struct create_esync_request, max) == 24 );
-C_ASSERT( sizeof(struct create_esync_request) == 32 );
-C_ASSERT( offsetof(struct create_esync_reply, handle) == 8 );
-C_ASSERT( offsetof(struct create_esync_reply, type) == 12 );
-C_ASSERT( offsetof(struct create_esync_reply, shm_idx) == 16 );
-C_ASSERT( sizeof(struct create_esync_reply) == 24 );
-C_ASSERT( offsetof(struct open_esync_request, access) == 12 );
-C_ASSERT( offsetof(struct open_esync_request, attributes) == 16 );
-C_ASSERT( offsetof(struct open_esync_request, rootdir) == 20 );
-C_ASSERT( offsetof(struct open_esync_request, type) == 24 );
-C_ASSERT( sizeof(struct open_esync_request) == 32 );
-C_ASSERT( offsetof(struct open_esync_reply, handle) == 8 );
-C_ASSERT( offsetof(struct open_esync_reply, type) == 12 );
-C_ASSERT( offsetof(struct open_esync_reply, shm_idx) == 16 );
-C_ASSERT( sizeof(struct open_esync_reply) == 24 );
-C_ASSERT( offsetof(struct get_esync_fd_request, handle) == 12 );
-C_ASSERT( sizeof(struct get_esync_fd_request) == 16 );
-C_ASSERT( offsetof(struct get_esync_fd_reply, type) == 8 );
-C_ASSERT( offsetof(struct get_esync_fd_reply, shm_idx) == 12 );
-C_ASSERT( sizeof(struct get_esync_fd_reply) == 16 );
-C_ASSERT( offsetof(struct esync_msgwait_request, in_msgwait) == 12 );
-C_ASSERT( sizeof(struct esync_msgwait_request) == 16 );
+C_ASSERT( sizeof(struct get_linux_sync_device_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_device_reply, handle) == 8 );
+C_ASSERT( sizeof(struct get_linux_sync_device_reply) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_request, handle) == 12 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_request) == 16 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, handle) == 8 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, type) == 12 );
+C_ASSERT( offsetof(struct get_linux_sync_obj_reply, access) == 16 );
+C_ASSERT( sizeof(struct get_linux_sync_obj_reply) == 24 );
+C_ASSERT( sizeof(struct select_inproc_queue_request) == 16 );
+C_ASSERT( offsetof(struct unselect_inproc_queue_request, signaled) == 12 );
+C_ASSERT( sizeof(struct unselect_inproc_queue_request) == 16 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_request, enable) == 12 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_request, delay) == 16 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_request, period) == 20 );
 C_ASSERT( sizeof(struct set_keyboard_repeat_request) == 24 );
 C_ASSERT( offsetof(struct set_keyboard_repeat_reply, enable) == 8 );
 C_ASSERT( sizeof(struct set_keyboard_repeat_reply) == 16 );
-C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_request) == 16 );
+C_ASSERT( offsetof(struct get_inproc_alert_event_reply, handle) == 8 );
+C_ASSERT( sizeof(struct get_inproc_alert_event_reply) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 5b24584..8f2ea61 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -3364,52 +3364,34 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req
     fprintf( stderr, " handle=%04x", req->handle );
 }
 
-static void dump_create_esync_request( const struct create_esync_request *req )
+static void dump_get_linux_sync_device_request( const struct get_linux_sync_device_request *req )
 {
-    fprintf( stderr, " access=%08x", req->access );
-    fprintf( stderr, ", initval=%d", req->initval );
-    fprintf( stderr, ", type=%d", req->type );
-    fprintf( stderr, ", max=%d", req->max );
-    dump_varargs_object_attributes( ", objattr=", cur_size );
 }
 
-static void dump_create_esync_reply( const struct create_esync_reply *req )
+static void dump_get_linux_sync_device_reply( const struct get_linux_sync_device_reply *req )
 {
     fprintf( stderr, " handle=%04x", req->handle );
-    fprintf( stderr, ", type=%d", req->type );
-    fprintf( stderr, ", shm_idx=%08x", req->shm_idx );
-}
-
-static void dump_open_esync_request( const struct open_esync_request *req )
-{
-    fprintf( stderr, " access=%08x", req->access );
-    fprintf( stderr, ", attributes=%08x", req->attributes );
-    fprintf( stderr, ", rootdir=%04x", req->rootdir );
-    fprintf( stderr, ", type=%d", req->type );
-    dump_varargs_unicode_str( ", name=", cur_size );
 }
 
-static void dump_open_esync_reply( const struct open_esync_reply *req )
+static void dump_get_linux_sync_obj_request( const struct get_linux_sync_obj_request *req )
 {
     fprintf( stderr, " handle=%04x", req->handle );
-    fprintf( stderr, ", type=%d", req->type );
-    fprintf( stderr, ", shm_idx=%08x", req->shm_idx );
 }
 
-static void dump_get_esync_fd_request( const struct get_esync_fd_request *req )
+static void dump_get_linux_sync_obj_reply( const struct get_linux_sync_obj_reply *req )
 {
     fprintf( stderr, " handle=%04x", req->handle );
+    fprintf( stderr, ", type=%d", req->type );
+    fprintf( stderr, ", access=%08x", req->access );
 }
 
-static void dump_get_esync_fd_reply( const struct get_esync_fd_reply *req )
+static void dump_select_inproc_queue_request( const struct select_inproc_queue_request *req )
 {
-    fprintf( stderr, " type=%d", req->type );
-    fprintf( stderr, ", shm_idx=%08x", req->shm_idx );
 }
 
-static void dump_esync_msgwait_request( const struct esync_msgwait_request *req )
+static void dump_unselect_inproc_queue_request( const struct unselect_inproc_queue_request *req )
 {
-    fprintf( stderr, " in_msgwait=%d", req->in_msgwait );
+    fprintf( stderr, " signaled=%d", req->signaled );
 }
 
 static void dump_set_keyboard_repeat_request( const struct set_keyboard_repeat_request *req )
@@ -3424,10 +3406,15 @@ static void dump_set_keyboard_repeat_reply( const struct set_keyboard_repeat_rep
     fprintf( stderr, " enable=%d", req->enable );
 }
 
-static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request *req )
+static void dump_get_inproc_alert_event_request( const struct get_inproc_alert_event_request *req )
 {
 }
 
+static void dump_get_inproc_alert_event_reply( const struct get_inproc_alert_event_reply *req )
+{
+    fprintf( stderr, " handle=%04x", req->handle );
+}
+
 typedef void (*dump_func)( const void *req );
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] =
@@ -3725,12 +3712,12 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] =
     (dump_func)dump_resume_process_request,
     (dump_func)dump_get_next_process_request,
     (dump_func)dump_get_next_thread_request,
-    (dump_func)dump_create_esync_request,
-    (dump_func)dump_open_esync_request,
-    (dump_func)dump_get_esync_fd_request,
-    (dump_func)dump_esync_msgwait_request,
+    (dump_func)dump_get_linux_sync_device_request,
+    (dump_func)dump_get_linux_sync_obj_request,
+    (dump_func)dump_select_inproc_queue_request,
+    (dump_func)dump_unselect_inproc_queue_request,
     (dump_func)dump_set_keyboard_repeat_request,
-    (dump_func)dump_get_esync_apc_fd_request,
+    (dump_func)dump_get_inproc_alert_event_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
@@ -4028,12 +4015,12 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] =
     NULL,
     (dump_func)dump_get_next_process_reply,
     (dump_func)dump_get_next_thread_reply,
-    (dump_func)dump_create_esync_reply,
-    (dump_func)dump_open_esync_reply,
-    (dump_func)dump_get_esync_fd_reply,
+    (dump_func)dump_get_linux_sync_device_reply,
+    (dump_func)dump_get_linux_sync_obj_reply,
     NULL,
-    (dump_func)dump_set_keyboard_repeat_reply,
     NULL,
+    (dump_func)dump_set_keyboard_repeat_reply,
+    (dump_func)dump_get_inproc_alert_event_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] =
@@ -4331,12 +4318,12 @@ static const char * const req_names[REQ_NB_REQUESTS] =
     "resume_process",
     "get_next_process",
     "get_next_thread",
-    "create_esync",
-    "open_esync",
-    "get_esync_fd",
-    "esync_msgwait",
+    "get_linux_sync_device",
+    "get_linux_sync_obj",
+    "select_inproc_queue",
+    "unselect_inproc_queue",
     "set_keyboard_repeat",
-    "get_esync_apc_fd",
+    "get_inproc_alert_event",
 };
 
 static const struct
diff --git a/server/semaphore.c b/server/semaphore.c
index e3889f2..bff55b1 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -55,12 +55,15 @@ struct semaphore
     struct object  obj;    /* object header */
     unsigned int   count;  /* current count */
     unsigned int   max;    /* maximum possible count */
+    struct inproc_sync *inproc_sync; /* fast synchronization object */
 };
 
 static void semaphore_dump( struct object *obj, int verbose );
 static int semaphore_signaled( struct object *obj, struct wait_queue_entry *entry );
 static void semaphore_satisfied( struct object *obj, struct wait_queue_entry *entry );
 static int semaphore_signal( struct object *obj, unsigned int access );
+static struct inproc_sync *semaphore_get_inproc_sync( struct object *obj );
+static void semaphore_destroy( struct object *obj );
 
 static const struct object_ops semaphore_ops =
 {
@@ -70,7 +73,6 @@ static const struct object_ops semaphore_ops =
     add_queue,                     /* add_queue */
     remove_queue,                  /* remove_queue */
     semaphore_signaled,            /* signaled */
-    NULL,                          /* get_esync_fd */
     semaphore_satisfied,           /* satisfied */
     semaphore_signal,              /* signal */
     no_get_fd,                     /* get_fd */
@@ -83,8 +85,9 @@ static const struct object_ops semaphore_ops =
     default_unlink_name,           /* unlink_name */
     no_open_file,                  /* open_file */
     no_kernel_obj_list,            /* get_kernel_obj_list */
+    semaphore_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,               /* close_handle */
-    no_destroy                     /* destroy */
+    semaphore_destroy              /* destroy */
 };
 
 
@@ -106,6 +109,7 @@ static struct semaphore *create_semaphore( struct object *root, const struct uni
             /* initialize it if it didn't already exist */
             sem->count = initial;
             sem->max   = max;
+            sem->inproc_sync = NULL;
         }
     }
     return sem;
@@ -168,6 +172,27 @@ static int semaphore_signal( struct object *obj, unsigned int access )
     return release_semaphore( sem, 1, NULL );
 }
 
+static struct inproc_sync *semaphore_get_inproc_sync( struct object *obj )
+{
+    struct semaphore *semaphore = (struct semaphore *)obj;
+
+    /* This state will always be the state that the semaphore was created with.
+     * We could create the inproc_sync at creation time to make this clearer,
+     * but some broken programs create hundreds of thousands of handles which
+     * they never use, and we want to avoid wasting memory or fds in that case. */
+    if (!semaphore->inproc_sync)
+        semaphore->inproc_sync = create_inproc_semaphore( semaphore->count, semaphore->max );
+    if (semaphore->inproc_sync) grab_object( semaphore->inproc_sync );
+    return semaphore->inproc_sync;
+}
+
+static void semaphore_destroy( struct object *obj )
+{
+    struct semaphore *semaphore = (struct semaphore *)obj;
+
+    if (semaphore->inproc_sync) release_object( semaphore->inproc_sync );
+}
+
 /* create a semaphore */
 DECL_HANDLER(create_semaphore)
 {
diff --git a/server/serial.c b/server/serial.c
index 0d49a99..9375332 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -91,7 +91,6 @@ static const struct object_ops serial_ops =
     add_queue,                    /* add_queue */
     remove_queue,                 /* remove_queue */
     default_fd_signaled,          /* signaled */
-    NULL,                         /* get_esync_fd */
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     serial_get_fd,                /* get_fd */
@@ -104,6 +103,7 @@ static const struct object_ops serial_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     serial_destroy                /* destroy */
 };
diff --git a/server/signal.c b/server/signal.c
index 55cd6aa..85d16ba 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -62,7 +62,6 @@ static const struct object_ops handler_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -75,6 +74,7 @@ static const struct object_ops handler_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,       /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     handler_destroy           /* destroy */
 };
diff --git a/server/sock.c b/server/sock.c
index ea5948a..3f042ac 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -476,7 +476,6 @@ static const struct object_ops sock_ops =
     add_queue,                    /* add_queue */
     remove_queue,                 /* remove_queue */
     default_fd_signaled,          /* signaled */
-    NULL,                         /* get_esync_fd */
     no_satisfied,                 /* satisfied */
     no_signal,                    /* signal */
     sock_get_fd,                  /* get_fd */
@@ -489,6 +488,7 @@ static const struct object_ops sock_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    default_fd_get_inproc_sync,   /* get_inproc_sync */
     sock_close_handle,            /* close_handle */
     sock_destroy                  /* destroy */
 };
@@ -3788,7 +3788,6 @@ static const struct object_ops ifchange_ops =
     no_add_queue,            /* add_queue */
     NULL,                    /* remove_queue */
     NULL,                    /* signaled */
-    NULL,                    /* get_esync_fd */
     no_satisfied,            /* satisfied */
     no_signal,               /* signal */
     ifchange_get_fd,         /* get_fd */
@@ -3801,6 +3800,7 @@ static const struct object_ops ifchange_ops =
     NULL,                    /* unlink_name */
     no_open_file,            /* open_file */
     no_kernel_obj_list,      /* get_kernel_obj_list */
+    no_get_inproc_sync,      /* get_inproc_sync */
     no_close_handle,         /* close_handle */
     ifchange_destroy         /* destroy */
 };
@@ -4010,7 +4010,6 @@ static const struct object_ops socket_device_ops =
     no_add_queue,               /* add_queue */
     NULL,                       /* remove_queue */
     NULL,                       /* signaled */
-    NULL,                       /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -4023,6 +4022,7 @@ static const struct object_ops socket_device_ops =
     default_unlink_name,        /* unlink_name */
     socket_device_open_file,    /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,      /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
diff --git a/server/symlink.c b/server/symlink.c
index 2dd9c6a..f0314f2 100644
--- a/server/symlink.c
+++ b/server/symlink.c
@@ -71,7 +71,6 @@ static const struct object_ops symlink_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -84,6 +83,7 @@ static const struct object_ops symlink_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,              /* close_handle */
     symlink_destroy               /* destroy */
 };
diff --git a/server/thread.c b/server/thread.c
index 989db31..9b4ce24 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -59,7 +59,6 @@
 #include "request.h"
 #include "user.h"
 #include "security.h"
-#include "esync.h"
 
 
 /* thread queues */
@@ -106,7 +105,6 @@ static const struct object_ops thread_apc_ops =
     add_queue,                  /* add_queue */
     remove_queue,               /* remove_queue */
     thread_apc_signaled,        /* signaled */
-    NULL,                       /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -119,6 +117,7 @@ static const struct object_ops thread_apc_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     thread_apc_destroy          /* destroy */
 };
@@ -149,7 +148,6 @@ static const struct object_ops context_ops =
     add_queue,                  /* add_queue */
     remove_queue,               /* remove_queue */
     context_signaled,           /* signaled */
-    NULL,                       /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -162,6 +160,7 @@ static const struct object_ops context_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     no_kernel_obj_list,         /* get_kernel_obj_list */
+    no_get_inproc_sync,         /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     no_destroy                  /* destroy */
 };
@@ -186,10 +185,10 @@ struct type_descr thread_type =
 
 static void dump_thread( struct object *obj, int verbose );
 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 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 );
+static struct inproc_sync *thread_get_inproc_sync( struct object *obj );
 static void destroy_thread( struct object *obj );
 
 static const struct object_ops thread_ops =
@@ -200,7 +199,6 @@ static const struct object_ops thread_ops =
     add_queue,                  /* add_queue */
     remove_queue,               /* remove_queue */
     thread_signaled,            /* signaled */
-    thread_get_esync_fd,        /* get_esync_fd */
     no_satisfied,               /* satisfied */
     no_signal,                  /* signal */
     no_get_fd,                  /* get_fd */
@@ -213,6 +211,7 @@ static const struct object_ops thread_ops =
     NULL,                       /* unlink_name */
     no_open_file,               /* open_file */
     thread_get_kernel_obj_list, /* get_kernel_obj_list */
+    thread_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,            /* close_handle */
     destroy_thread              /* destroy */
 };
@@ -396,8 +395,6 @@ static inline void init_thread_structure( struct thread *thread )
     thread->context         = NULL;
     thread->teb             = 0;
     thread->entry_point     = 0;
-    thread->esync_fd        = -1;
-    thread->esync_apc_fd    = -1;
     thread->system_regs     = 0;
     thread->queue           = NULL;
     thread->wait            = NULL;
@@ -418,7 +415,8 @@ static inline void init_thread_structure( struct thread *thread )
     thread->token           = NULL;
     thread->desc            = NULL;
     thread->desc_len        = 0;
-    thread->exit_poll       = NULL;
+    thread->inproc_sync     = NULL;
+    thread->inproc_alert_event = NULL;
 
     thread->creation_time = current_time;
     thread->exit_time     = 0;
@@ -546,12 +544,6 @@ struct thread *create_thread( int fd, struct process *process, const struct secu
         }
     }
 
-    if (do_esync())
-    {
-        thread->esync_fd = esync_create_fd( 0, 0 );
-        thread->esync_apc_fd = esync_create_fd( 0, 0 );
-    }
-
     set_fd_events( thread->request_fd, POLLIN );  /* start listening to events */
     add_process_thread( thread->process, thread );
     return thread;
@@ -576,6 +568,16 @@ static struct list *thread_get_kernel_obj_list( struct object *obj )
     return &thread->kernel_object;
 }
 
+static struct inproc_sync *thread_get_inproc_sync( struct object *obj )
+{
+    struct thread *thread = (struct thread *)obj;
+
+    if (!thread->inproc_sync)
+        thread->inproc_sync = create_inproc_event( INPROC_SYNC_MANUAL_SERVER, thread->state == TERMINATED );
+    if (thread->inproc_sync) grab_object( thread->inproc_sync );
+    return thread->inproc_sync;
+}
+
 /* cleanup everything that is no longer needed by a dead thread */
 /* used by destroy_thread and kill_thread */
 static void cleanup_thread( struct thread *thread )
@@ -629,12 +631,10 @@ static void destroy_thread( struct object *obj )
     list_remove( &thread->entry );
     cleanup_thread( thread );
     release_object( thread->process );
-    if (thread->exit_poll) remove_timeout_user( thread->exit_poll );
     if (thread->id) free_ptid( thread->id );
     if (thread->token) release_object( thread->token );
-
-    if (do_esync())
-        close( thread->esync_fd );
+    if (thread->inproc_sync) release_object( thread->inproc_sync );
+    if (thread->inproc_alert_event) release_object( thread->inproc_alert_event );
 }
 
 /* dump a thread on stdout for debugging purposes */
@@ -650,14 +650,7 @@ static void dump_thread( struct object *obj, int verbose )
 static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
 {
     struct thread *mythread = (struct thread *)obj;
-    return mythread->state == TERMINATED && !mythread->exit_poll;
-}
-
-static int thread_get_esync_fd( struct object *obj, enum esync_type *type )
-{
-    struct thread *thread = (struct thread *)obj;
-    *type = ESYNC_MANUAL_SERVER;
-    return thread->esync_fd;
+    return (mythread->state == TERMINATED);
 }
 
 static unsigned int thread_map_access( struct object *obj, unsigned int access )
@@ -1279,9 +1272,6 @@ void wake_up( struct object *obj, int max )
     struct list *ptr;
     int ret;
 
-    if (do_esync())
-        esync_wake_up( obj );
-
     LIST_FOR_EACH( ptr, &obj->wait_queue )
     {
         struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry );
@@ -1369,8 +1359,8 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr
     {
         wake_thread( thread );
 
-        if (do_esync() && queue == &thread->user_apc)
-            esync_wake_fd( thread->esync_apc_fd );
+        if (apc->call.type == APC_USER && thread->inproc_alert_event)
+            set_event( thread->inproc_alert_event );
     }
 
     return 1;
@@ -1403,6 +1393,8 @@ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_ty
         apc->executed = 1;
         wake_up( &apc->obj, 0 );
         release_object( apc );
+        if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+            reset_event( thread->inproc_alert_event );
         return;
     }
 }
@@ -1417,11 +1409,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system
     {
         apc = LIST_ENTRY( ptr, struct thread_apc, entry );
         list_remove( ptr );
-    }
-
-    if (do_esync() && list_empty( &thread->system_apc ) && list_empty( &thread->user_apc ))
-        esync_clear( thread->esync_apc_fd );
 
+        if (list_empty( &thread->user_apc ) && thread->inproc_alert_event)
+            reset_event( thread->inproc_alert_event );
+    }
     return apc;
 }
 
@@ -1498,26 +1489,6 @@ int thread_get_inflight_fd( struct thread *thread, int client )
     return -1;
 }
 
-static void check_terminated( void *arg )
-{
-    struct thread *thread = arg;
-    assert( thread->obj.ops == &thread_ops );
-    assert( thread->state == TERMINATED );
-
-    /* don't wake up until the thread is really dead, to avoid race conditions */
-    if (thread->unix_tid != -1 && !kill( thread->unix_tid, 0 ))
-    {
-        thread->exit_poll = add_timeout_user( -TICKS_PER_SEC / 1000, check_terminated, thread );
-        return;
-    }
-
-    /* grab reference since object can be destroyed while trying to wake up */
-    grab_object( &thread->obj );
-    thread->exit_poll = NULL;
-    wake_up( &thread->obj, 0 );
-    release_object( &thread->obj );
-}
-
 /* kill a thread on the spot */
 void kill_thread( struct thread *thread, int violent_death )
 {
@@ -1537,14 +1508,9 @@ void kill_thread( struct thread *thread, int violent_death )
     }
     kill_console_processes( thread, 0 );
     abandon_mutexes( thread );
-    if (do_esync())
-        esync_abandon_mutexes( thread );
-    if (violent_death)
-    {
-        send_thread_signal( thread, SIGQUIT );
-        check_terminated( thread );
-    }
-    else wake_up( &thread->obj, 0 );
+    wake_up( &thread->obj, 0 );
+    set_inproc_event( thread->inproc_sync );
+    if (violent_death) send_thread_signal( thread, SIGQUIT );
     cleanup_thread( thread );
     remove_process_thread( thread->process, thread );
     release_object( thread );
@@ -2290,3 +2256,12 @@ DECL_HANDLER(get_next_thread)
     set_error( STATUS_NO_MORE_ENTRIES );
     release_object( process );
 }
+
+DECL_HANDLER(get_inproc_alert_event)
+{
+    if (!current->inproc_alert_event)
+        current->inproc_alert_event = create_event( NULL, NULL, 0, 1, !list_empty( &current->user_apc ), NULL );
+
+    if (current->inproc_alert_event)
+        reply->handle = alloc_handle( current->process, current->inproc_alert_event, SYNCHRONIZE, 0 );
+}
diff --git a/server/thread.h b/server/thread.h
index afe7db1..4641ad0 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -56,8 +56,6 @@ 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) */
     unsigned int           system_regs;   /* which system regs have been set */
     struct msg_queue      *queue;         /* message queue */
     struct thread_wait    *wait;          /* current wait condition if sleeping */
@@ -95,7 +93,8 @@ struct thread
     data_size_t            desc_len;      /* thread description length in bytes */
     WCHAR                 *desc;          /* thread description string */
     struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */
-    struct timeout_user   *exit_poll;     /* poll if the thread/process has exited already */
+    struct inproc_sync    *inproc_sync;   /* in-process synchronization object */
+    struct event          *inproc_alert_event; /* in-process synchronization alert event */
 };
 
 extern struct thread *current;
diff --git a/server/timer.c b/server/timer.c
index b4ab611..397d045 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -35,7 +35,6 @@
 #include "file.h"
 #include "handle.h"
 #include "request.h"
-#include "esync.h"
 
 static const WCHAR timer_name[] = {'T','i','m','e','r'};
 
@@ -62,13 +61,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 inproc_sync  *inproc_sync;   /* in-process synchronization object */
 };
 
 static void timer_dump( struct object *obj, int verbose );
 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 void timer_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static struct inproc_sync *timer_get_inproc_sync( struct object *obj );
 static void timer_destroy( struct object *obj );
 
 static const struct object_ops timer_ops =
@@ -79,7 +78,6 @@ static const struct object_ops timer_ops =
     add_queue,                 /* add_queue */
     remove_queue,              /* remove_queue */
     timer_signaled,            /* signaled */
-    timer_get_esync_fd,        /* get_esync_fd */
     timer_satisfied,           /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -92,6 +90,7 @@ static const struct object_ops timer_ops =
     default_unlink_name,       /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    timer_get_inproc_sync,     /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     timer_destroy              /* destroy */
 };
@@ -114,10 +113,7 @@ static struct timer *create_timer( struct object *root, const struct unicode_str
             timer->period   = 0;
             timer->timeout  = NULL;
             timer->thread   = NULL;
-            timer->esync_fd = -1;
-
-            if (do_esync())
-                timer->esync_fd = esync_create_fd( 0, 0 );
+            timer->inproc_sync = NULL;
         }
     }
     return timer;
@@ -159,6 +155,7 @@ static void timer_callback( void *private )
     /* wake up waiters */
     timer->signaled = 1;
     wake_up( &timer->obj, 0 );
+    set_inproc_event( timer->inproc_sync );
 }
 
 /* cancel a running timer */
@@ -189,9 +186,7 @@ static int set_timer( struct timer *timer, timeout_t expire, unsigned int period
     {
         period = 0;  /* period doesn't make any sense for a manual timer */
         timer->signaled = 0;
-
-        if (do_esync())
-            esync_clear( timer->esync_fd );
+        reset_inproc_event( timer->inproc_sync );
     }
     timer->when     = (expire <= 0) ? expire - monotonic_time : max( expire, current_time );
     timer->period   = period;
@@ -219,18 +214,24 @@ 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 void timer_satisfied( struct object *obj, struct wait_queue_entry *entry )
 {
     struct timer *timer = (struct timer *)obj;
-    *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER;
-    return timer->esync_fd;
+    assert( obj->ops == &timer_ops );
+    if (!timer->manual) timer->signaled = 0;
 }
 
-static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry )
+static struct inproc_sync *timer_get_inproc_sync( struct object *obj )
 {
     struct timer *timer = (struct timer *)obj;
-    assert( obj->ops == &timer_ops );
-    if (!timer->manual) timer->signaled = 0;
+
+    if (!timer->inproc_sync)
+    {
+        enum inproc_sync_type type = timer->manual ? INPROC_SYNC_MANUAL_SERVER : INPROC_SYNC_AUTO_SERVER;
+        timer->inproc_sync = create_inproc_event( type, timer->signaled );
+    }
+    if (timer->inproc_sync) grab_object( timer->inproc_sync );
+    return timer->inproc_sync;
 }
 
 static void timer_destroy( struct object *obj )
@@ -240,7 +241,7 @@ 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()) close( timer->esync_fd );
+    if (timer->inproc_sync) release_object( timer->inproc_sync );
 }
 
 /* create a timer */
diff --git a/server/token.c b/server/token.c
index b638ed1..844ad28 100644
--- a/server/token.c
+++ b/server/token.c
@@ -145,7 +145,6 @@ static const struct object_ops token_ops =
     no_add_queue,              /* add_queue */
     NULL,                      /* remove_queue */
     NULL,                      /* signaled */
-    NULL,                      /* get_esync_fd */
     NULL,                      /* satisfied */
     no_signal,                 /* signal */
     no_get_fd,                 /* get_fd */
@@ -158,6 +157,7 @@ static const struct object_ops token_ops =
     NULL,                      /* unlink_name */
     no_open_file,              /* open_file */
     no_kernel_obj_list,        /* get_kernel_obj_list */
+    no_get_inproc_sync,        /* get_inproc_sync */
     no_close_handle,           /* close_handle */
     token_destroy              /* destroy */
 };
diff --git a/server/window.c b/server/window.c
index 8c416d8..6c9820c 100644
--- a/server/window.c
+++ b/server/window.c
@@ -107,7 +107,6 @@ static const struct object_ops window_ops =
     no_add_queue,             /* add_queue */
     NULL,                     /* remove_queue */
     NULL,                     /* signaled */
-    NULL,                     /* get_esync_fd */
     NULL,                     /* satisfied */
     no_signal,                /* signal */
     no_get_fd,                /* get_fd */
@@ -120,6 +119,7 @@ static const struct object_ops window_ops =
     NULL,                     /* unlink_name */
     no_open_file,             /* open_file */
     no_kernel_obj_list,       /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     no_close_handle,          /* close_handle */
     window_destroy            /* destroy */
 };
diff --git a/server/winstation.c b/server/winstation.c
index 126b70d..4b8a497 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -76,7 +76,6 @@ static const struct object_ops winstation_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -89,6 +88,7 @@ static const struct object_ops winstation_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     winstation_close_handle,      /* close_handle */
     winstation_destroy            /* destroy */
 };
@@ -117,7 +117,6 @@ static const struct object_ops desktop_ops =
     no_add_queue,                 /* add_queue */
     NULL,                         /* remove_queue */
     NULL,                         /* signaled */
-    NULL,                         /* get_esync_fd */
     NULL,                         /* satisfied */
     no_signal,                    /* signal */
     no_get_fd,                    /* get_fd */
@@ -130,6 +129,7 @@ static const struct object_ops desktop_ops =
     default_unlink_name,          /* unlink_name */
     no_open_file,                 /* open_file */
     no_kernel_obj_list,           /* get_kernel_obj_list */
+    no_get_inproc_sync,           /* get_inproc_sync */
     desktop_close_handle,         /* close_handle */
     desktop_destroy               /* destroy */
 };
-- 
2.48.1

openSUSE Build Service is sponsored by