eventfd_synchronization: Add patch set.

This commit is contained in:
Zebediah Figura 2019-04-03 21:40:49 -05:00
parent 8ce5fe8929
commit 553986fdfb
85 changed files with 11099 additions and 339 deletions

View File

@ -0,0 +1,54 @@
From 2cc1bac9dcbe9d52e395baea3811bfbb38c1ec0b Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 10:44:49 -0500
Subject: [PATCH 01/83] configure: Check for sys/eventfd.h, ppoll(), and
shm_open().
We use ppoll() instead of poll() for the better time granularity.
Although perhaps we shouldn't since the server doesn't do this.
---
configure.ac | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/configure.ac b/configure.ac
index b2c614726..b44729df7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -505,6 +505,7 @@ AC_CHECK_HEADERS(\
sys/elf32.h \
sys/epoll.h \
sys/event.h \
+ sys/eventfd.h \
sys/exec_elf.h \
sys/filio.h \
sys/inotify.h \
@@ -2245,6 +2246,7 @@ AC_CHECK_FUNCS(\
pipe2 \
poll \
port_create \
+ ppoll \
prctl \
pread \
proc_pidinfo \
@@ -2317,6 +2319,16 @@ AC_SEARCH_LIBS(clock_gettime, rt,
test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")])
LIBS=$ac_save_LIBS
+dnl Check for shm_open which may be in -lrt
+if test "$ac_cv_header_sys_mman_h" = "yes" -a "x$RT_LIBS" = "x"
+then
+ 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")])
+fi
+LIBS=$ac_save_LIBS
+
dnl **** Check for OpenLDAP ***
if test "x$with_ldap" != "xno"
then
--
2.20.1

View File

@ -0,0 +1,189 @@
From ed20373cc262b783cd7e04641618a732f33e8a51 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 20:09:59 -0500
Subject: [PATCH 02/83] server: Create server objects for eventfd-based
synchronization objects.
---
server/Makefile.in | 1 +
server/esync.c | 137 ++++++++++++++++++++++++++++++++++++++++++++
server/protocol.def | 10 ++++
3 files changed, 148 insertions(+)
create mode 100644 server/esync.c
diff --git a/server/Makefile.in b/server/Makefile.in
index d2715e8cb..4f8f10cd5 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -11,6 +11,7 @@ C_SRCS = \
debugger.c \
device.c \
directory.c \
+ esync.c \
event.c \
fd.c \
file.c \
diff --git a/server/esync.c b/server/esync.c
new file mode 100644
index 000000000..147fb7724
--- /dev/null
+++ b/server/esync.c
@@ -0,0 +1,137 @@
+/*
+ * 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 "wine/port.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_SYS_EVENTFD_H
+# include <sys/eventfd.h>
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winternl.h"
+
+#include "handle.h"
+#include "request.h"
+#include "file.h"
+
+struct esync
+{
+ struct object obj; /* object header */
+ int fd; /* eventfd file descriptor */
+};
+
+static void esync_dump( struct object *obj, int verbose );
+static void esync_destroy( struct object *obj );
+
+static const struct object_ops esync_ops =
+{
+ sizeof(struct esync), /* size */
+ esync_dump, /* dump */
+ no_get_type, /* get_type */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ no_signal, /* signal */
+ no_get_fd, /* get_fd */
+ no_map_access, /* map_access */
+ default_get_sd, /* get_sd */
+ default_set_sd, /* set_sd */
+ 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 void esync_destroy( struct object *obj )
+{
+ struct esync *esync = (struct esync *)obj;
+ close( esync->fd );
+}
+
+struct esync *create_esync( struct object *root, const struct unicode_str *name,
+ unsigned int attr, int initval, int flags,
+ 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)
+ {
+ /* initialize it if it didn't already exist */
+ esync->fd = eventfd( initval, flags | EFD_CLOEXEC | EFD_NONBLOCK );
+ if (esync->fd == -1)
+ {
+ perror( "eventfd" );
+ file_set_error();
+ release_object( esync );
+ return NULL;
+ }
+ }
+ }
+ return esync;
+#else
+ /* FIXME: Provide a fallback implementation using pipe(). */
+ set_error( STATUS_NOT_IMPLEMENTED );
+ return NULL;
+#endif
+}
+
+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 (!objattr) return;
+
+ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->flags, 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 );
+
+ send_client_fd( current->process, esync->fd, reply->handle );
+ release_object( esync );
+ }
+
+ if (root) release_object( root );
+}
diff --git a/server/protocol.def b/server/protocol.def
index c3751e609..a56f098ab 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4041,3 +4041,13 @@ struct handle_info
@REQ(resume_process)
obj_handle_t handle; /* process handle */
@END
+
+/* Create a new eventfd-based synchronization object */
+@REQ(create_esync)
+ unsigned int access; /* wanted access rights */
+ int initval; /* initial value */
+ int flags; /* flags (EFD_SEMAPHORE or 0) */
+ VARARG(objattr,object_attributes); /* object attributes */
+@REPLY
+ obj_handle_t handle; /* handle to the object */
+@END
--
2.20.1

View File

@ -0,0 +1,292 @@
From 4df197cdb567f731bec385786ccda35499d656b4 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 20:29:21 -0500
Subject: [PATCH 03/83] ntdll: Create eventfd-based objects for semaphores.
This patch break things, of course. That is fine. Its purpose is to prevent a Massive Patch Munge.
---
dlls/ntdll/Makefile.in | 1 +
dlls/ntdll/esync.c | 141 ++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 32 +++++++++
dlls/ntdll/ntdll_misc.h | 1 +
dlls/ntdll/server.c | 7 +-
dlls/ntdll/sync.c | 5 ++
6 files changed, 184 insertions(+), 3 deletions(-)
create mode 100644 dlls/ntdll/esync.c
create mode 100644 dlls/ntdll/esync.h
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index ed4bb94e4..b75e8308a 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -15,6 +15,7 @@ C_SRCS = \
directory.c \
env.c \
error.c \
+ esync.c \
exception.c \
file.c \
handletable.c \
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
new file mode 100644
index 000000000..da35bdf85
--- /dev/null
+++ b/dlls/ntdll/esync.c
@@ -0,0 +1,141 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_EVENTFD_H
+# include <sys/eventfd.h>
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#define NONAMELESSUNION
+#include "windef.h"
+#include "winternl.h"
+#include "wine/server.h"
+#include "wine/debug.h"
+
+#include "ntdll_misc.h"
+#include "esync.h"
+
+#ifndef EFD_SEMAPHORE
+#define EFD_SEMAPHORE 1
+#endif
+
+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") != NULL);
+
+ return do_esync_cached;
+#else
+ static int once;
+ if (!once++)
+ FIXME("eventfd not supported on this platform.\n");
+ return 0;
+#endif
+}
+
+enum esync_type
+{
+ ESYNC_SEMAPHORE = 1,
+};
+
+struct esync
+{
+ enum esync_type type;
+ int fd;
+};
+
+struct semaphore
+{
+ struct esync obj;
+ int max;
+};
+
+static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, int initval, int flags)
+{
+ NTSTATUS ret;
+ data_size_t len;
+ struct object_attributes *objattr;
+ obj_handle_t fd_handle;
+ sigset_t sigset;
+
+ 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_section, &sigset );
+ SERVER_START_REQ( create_esync )
+ {
+ req->access = access;
+ req->initval = initval;
+ req->flags = flags;
+ 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 );
+ *fd = receive_fd( &fd_handle );
+ assert( wine_server_ptr_handle(fd_handle) == *handle );
+ }
+ }
+ SERVER_END_REQ;
+ server_leave_uninterrupted_section( &fd_cache_section, &sigset );
+
+ TRACE("-> handle %p, fd %d.\n", *handle, *fd);
+
+ RtlFreeHeap( GetProcessHeap(), 0, objattr );
+ return ret;
+}
+
+extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
+{
+ struct semaphore *semaphore;
+ NTSTATUS ret;
+ int fd = -1;
+
+ TRACE("name %s, initial %d, max %d.\n",
+ attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
+
+ ret = create_esync( &fd, handle, access, attr, initial, EFD_SEMAPHORE );
+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ {
+ semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
+ if (!semaphore)
+ return STATUS_NO_MEMORY;
+
+ semaphore->obj.type = ESYNC_SEMAPHORE;
+ semaphore->obj.fd = fd;
+ semaphore->max = max;
+ }
+
+ return ret;
+}
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
new file mode 100644
index 000000000..1a88170cf
--- /dev/null
+++ b/dlls/ntdll/esync.h
@@ -0,0 +1,32 @@
+/*
+ * 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) DECLSPEC_HIDDEN;
+
+extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN;
+
+
+/* We have to synchronize on the fd cache CS 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 CS to
+ * "server_fd_section" or something similar. */
+extern RTL_CRITICAL_SECTION fd_cache_section;
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 17d87cd52..dad0996c4 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -107,6 +107,7 @@ extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *
extern int server_remove_fd_from_cache( HANDLE handle ) DECLSPEC_HIDDEN;
extern int server_get_unix_fd( HANDLE handle, unsigned int access, int *unix_fd,
int *needs_close, enum server_fd_type *type, unsigned int *options ) DECLSPEC_HIDDEN;
+extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN;
extern int server_pipe( int fd[2] ) DECLSPEC_HIDDEN;
extern NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret,
data_size_t *ret_len ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index d2d238968..dcb355a65 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -81,6 +81,7 @@
#include "wine/server.h"
#include "wine/debug.h"
#include "ntdll_misc.h"
+#include "esync.h"
WINE_DEFAULT_DEBUG_CHANNEL(server);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
@@ -120,14 +121,14 @@ sigset_t server_block_set; /* signals to block during server calls */
static int fd_socket = -1; /* socket to exchange file descriptors with the server */
static pid_t server_pid;
-static RTL_CRITICAL_SECTION fd_cache_section;
+RTL_CRITICAL_SECTION fd_cache_section;
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &fd_cache_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": fd_cache_section") }
};
-static RTL_CRITICAL_SECTION fd_cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
+RTL_CRITICAL_SECTION fd_cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
/* atomically exchange a 64-bit value */
static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val )
@@ -773,7 +774,7 @@ void CDECL wine_server_send_fd( int fd )
*
* Receive a file descriptor passed from the server.
*/
-static int receive_fd( obj_handle_t *handle )
+int receive_fd( obj_handle_t *handle )
{
struct iovec vec;
struct msghdr msghdr;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index b74bebf08..c259267fb 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -59,7 +59,9 @@
#include "winternl.h"
#include "wine/server.h"
#include "wine/debug.h"
+
#include "ntdll_misc.h"
+#include "esync.h"
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
@@ -248,6 +250,9 @@ NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle,
if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount)
return STATUS_INVALID_PARAMETER;
+ if (do_esync())
+ return esync_create_semaphore( SemaphoreHandle, access, attr, InitialCount, MaximumCount );
+
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
SERVER_START_REQ( create_semaphore )
--
2.20.1

View File

@ -0,0 +1,123 @@
From 3f6f2c22a0ee8e7c9067b74aef67018ed0739484 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 20:45:57 -0500
Subject: [PATCH 04/83] ntdll: Store esync objects locally.
Slight tweak for optimization: return UINT_PTR instead, and remove a useless
cmpxchg.
---
dlls/ntdll/esync.c | 62 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index da35bdf85..2bb85d910 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -19,6 +19,7 @@
*/
#include "config.h"
+#include "wine/port.h"
#include <assert.h>
#include <stdarg.h>
@@ -26,6 +27,9 @@
#ifdef HAVE_SYS_EVENTFD_H
# include <sys/eventfd.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -34,6 +38,7 @@
#include "winternl.h"
#include "wine/server.h"
#include "wine/debug.h"
+#include "wine/library.h"
#include "ntdll_misc.h"
#include "esync.h"
@@ -78,6 +83,59 @@ struct semaphore
int max;
};
+/* 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 128
+
+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 BOOL add_to_list( HANDLE handle, struct esync *obj )
+{
+ 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 = wine_anon_mmap( NULL, ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync *),
+ PROT_READ | PROT_WRITE, 0 );
+ if (ptr == MAP_FAILED) return FALSE;
+ esync_list[entry] = ptr;
+ }
+ }
+
+ obj = interlocked_xchg_ptr((void **)&esync_list[entry][idx], obj);
+ assert(!obj);
+ return TRUE;
+}
+
+static void *esync_get_object( HANDLE handle )
+{
+ UINT_PTR entry, idx = handle_to_index( handle, &entry );
+
+ if (entry >= ESYNC_LIST_ENTRIES || !esync_list[entry]) return NULL;
+
+ return esync_list[entry][idx];
+}
+
+
static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, int initval, int flags)
{
@@ -115,7 +173,7 @@ static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
return ret;
}
-extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
+NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
struct semaphore *semaphore;
@@ -135,6 +193,8 @@ extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
semaphore->obj.type = ESYNC_SEMAPHORE;
semaphore->obj.fd = fd;
semaphore->max = max;
+
+ add_to_list( *handle, &semaphore->obj );
}
return ret;
--
2.20.1

View File

@ -0,0 +1,78 @@
From eb0273dfd273f1f6c48db45b444ec318fe514699 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 21:02:14 -0500
Subject: [PATCH 05/83] ntdll: Implement NtReleaseSemaphore().
---
dlls/ntdll/esync.c | 22 ++++++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/sync.c | 4 ++++
3 files changed, 27 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 2bb85d910..bca95b9b2 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -23,6 +23,7 @@
#include <assert.h>
#include <stdarg.h>
+#include <stdint.h>
#include <stdlib.h>
#ifdef HAVE_SYS_EVENTFD_H
# include <sys/eventfd.h>
@@ -199,3 +200,24 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
return ret;
}
+
+NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
+{
+ struct semaphore *semaphore = esync_get_object( handle );
+ uint64_t count64 = count;
+
+ TRACE("%p, %d, %p.\n", handle, count, prev);
+
+ if (!semaphore) return STATUS_INVALID_HANDLE;
+
+ if (prev)
+ {
+ FIXME("Can't write previous value.\n");
+ *prev = 1;
+ }
+
+ if (write( semaphore->obj.fd, &count64, sizeof(count64) ) == -1)
+ return FILE_GetNtStatus();
+
+ return STATUS_SUCCESS;
+}
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 1a88170cf..fec0b68e8 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -22,6 +22,7 @@ extern int do_esync(void) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN;
/* We have to synchronize on the fd cache CS so that our calls to receive_fd
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index c259267fb..93052ddb5 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -333,6 +333,10 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla
NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
{
NTSTATUS ret;
+
+ if (do_esync())
+ return esync_release_semaphore( handle, count, previous );
+
SERVER_START_REQ( release_semaphore )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,78 @@
From d101cc56af09470319046d570891e861d0a6154a Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 21:07:51 -0500
Subject: [PATCH 06/83] ntdll: Close esync objects.
---
dlls/ntdll/esync.c | 19 +++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/om.c | 4 ++++
3 files changed, 24 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index bca95b9b2..f7a427425 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -136,6 +136,25 @@ static void *esync_get_object( HANDLE handle )
return esync_list[entry][idx];
}
+NTSTATUS esync_close( HANDLE handle )
+{
+ UINT_PTR entry, idx = handle_to_index( handle, &entry );
+ struct esync *obj;
+
+ TRACE("%p.\n", handle);
+
+ if (entry < ESYNC_LIST_ENTRIES && esync_list[entry])
+ {
+ if ((obj = interlocked_xchg_ptr( (void **)&esync_list[entry][idx], 0 )))
+ {
+ close( obj->fd );
+ RtlFreeHeap( GetProcessHeap(), 0, obj );
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_INVALID_HANDLE;
+}
static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, int initval, int flags)
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index fec0b68e8..a22618de8 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -19,6 +19,7 @@
*/
extern int do_esync(void) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c
index b9fe302b1..ef2cb8b94 100644
--- a/dlls/ntdll/om.c
+++ b/dlls/ntdll/om.c
@@ -34,6 +34,7 @@
#include "windef.h"
#include "winternl.h"
#include "ntdll_misc.h"
+#include "esync.h"
#include "wine/server.h"
#include "wine/exception.h"
#include "wine/unicode.h"
@@ -446,6 +447,9 @@ NTSTATUS close_handle( HANDLE handle )
NTSTATUS ret;
int fd = server_remove_fd_from_cache( handle );
+ if (do_esync())
+ esync_close( handle );
+
SERVER_START_REQ( close_handle )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,225 @@
From f3596cd66ac694d4a7633f91bc53a2f44b2608cc Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 7 Jun 2018 21:23:52 -0500
Subject: [PATCH 07/83] ntdll: Implement waiting on esync objects.
This is the most basic case: WAIT_ANY. We use poll() (actually ppoll(), for
the better granularity) to select on all of the handles that we can.
---
dlls/ntdll/esync.c | 161 +++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 3 +
dlls/ntdll/sync.c | 7 ++
3 files changed, 171 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index f7a427425..0950d8b5a 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -22,6 +22,13 @@
#include "wine/port.h"
#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
@@ -240,3 +247,157 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
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 )
+{
+ 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;
+ return ppoll( fds, nfds, &tmo_p, NULL );
+#else
+ return poll( fds, nfds, timeleft / TICKSPERMSEC );
+#endif
+ }
+ else
+ return poll( fds, nfds, -1 );
+}
+
+/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we
+ * need to delegate to server_select(). */
+NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout )
+{
+ struct esync *objs[MAXIMUM_WAIT_OBJECTS];
+ struct pollfd fds[MAXIMUM_WAIT_OBJECTS];
+ int has_esync = 0, has_server = 0;
+ LONGLONG timeleft;
+ LARGE_INTEGER now;
+ ULONGLONG end;
+ int ret;
+ int i;
+
+ 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++)
+ {
+ objs[i] = esync_get_object( handles[i] );
+ if (objs[i])
+ has_esync = 1;
+ else
+ has_server = 1;
+ }
+
+ if (has_esync && has_server)
+ {
+ FIXME("Can't wait on esync and server objects at the same time!\n");
+ /* Wait on just the eventfds; it's the best we can do. */
+ }
+ else if (has_server)
+ {
+ /* It's just server objects, so delegate to the 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++)
+ DPRINTF(" %p", handles[i]);
+
+ if (!timeout)
+ DPRINTF(", timeout = INFINITE.\n");
+ else
+ {
+ timeleft = update_timeout( end );
+ DPRINTF(", timeout = %ld.%07ld sec.\n",
+ (long) timeleft / TICKSPERSEC, (long) timeleft % TICKSPERSEC);
+ }
+ }
+
+ if (wait_any || count == 1)
+ {
+ for (i = 0; i < count; i++)
+ {
+ fds[i].fd = objs[i] ? objs[i]->fd : -1;
+ fds[i].events = POLLIN;
+ }
+
+ while (1)
+ {
+ ret = do_poll( fds, count, timeout ? &end : NULL );
+ if (ret > 0)
+ {
+ /* Find out which object triggered the wait. */
+ for (i = 0; i < count; 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 (objs[i])
+ {
+ int64_t value;
+ ssize_t size;
+
+ 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);
+ return i;
+ }
+ }
+ }
+
+ /* If we got here, someone else stole (or reset, etc.) whatever
+ * we were waiting for. So keep waiting. */
+ NtQuerySystemTime( &now );
+ }
+ else if (ret == 0)
+ {
+ TRACE("Wait timed out.\n");
+ return STATUS_TIMEOUT;
+ }
+ else
+ {
+ ERR("ppoll failed: %s\n", strerror(errno));
+ return FILE_GetNtStatus();
+ }
+ }
+ }
+ else
+ {
+ FIXME("Wait-all not implemented.\n");
+ return STATUS_NOT_IMPLEMENTED;
+ }
+}
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index a22618de8..8f7f9b030 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -25,6 +25,9 @@ extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN;
extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
+
/* We have to synchronize on the fd cache CS so that our calls to receive_fd
* don't race with theirs. It looks weird, I know.
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 93052ddb5..40bc619a2 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -1089,6 +1089,13 @@ static NTSTATUS wait_objects( DWORD count, const HANDLE *handles,
if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
+ if (do_esync())
+ {
+ NTSTATUS ret = esync_wait_objects( count, handles, wait_any, alertable, timeout );
+ if (ret != STATUS_NOT_IMPLEMENTED)
+ 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] );
--
2.20.1

View File

@ -0,0 +1,100 @@
From 0ab9981b2309831fb12ee778e026cb1b4651dab6 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 15:33:46 -0500
Subject: [PATCH 08/83] ntdll: Create esync objects for events.
---
dlls/ntdll/esync.c | 34 ++++++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 39 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 0950d8b5a..ace2f4a45 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -77,6 +77,8 @@ int do_esync(void)
enum esync_type
{
ESYNC_SEMAPHORE = 1,
+ ESYNC_AUTO_EVENT,
+ ESYNC_MANUAL_EVENT,
};
struct esync
@@ -91,6 +93,11 @@ struct semaphore
int max;
};
+struct event
+{
+ struct esync obj;
+};
+
/* 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. */
@@ -248,6 +255,33 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial )
+{
+ struct event *event;
+ NTSTATUS ret;
+ int fd;
+
+ TRACE("name %s, %s-reset, initial %d.\n",
+ attr ? debugstr_us(attr->ObjectName) : "<no name>",
+ type == NotificationEvent ? "manual" : "auto", initial);
+
+ ret = create_esync( &fd, handle, access, attr, initial, 0 );
+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ {
+ event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
+ if (!event)
+ return STATUS_NO_MEMORY;
+
+ event->obj.type = (type == NotificationEvent ? ESYNC_MANUAL_EVENT : ESYNC_AUTO_EVENT);
+ event->obj.fd = fd;
+
+ add_to_list( *handle, &event->obj);
+ }
+
+ return ret;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 8f7f9b030..32a259e39 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -24,6 +24,8 @@ extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max) DECLSPEC_HIDDEN;
extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 40bc619a2..6f0e72125 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -365,6 +365,9 @@ NTSTATUS WINAPI NtCreateEvent( PHANDLE EventHandle, ACCESS_MASK DesiredAccess,
data_size_t len;
struct object_attributes *objattr;
+ if (do_esync())
+ return esync_create_event( EventHandle, DesiredAccess, attr, type, InitialState );
+
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
SERVER_START_REQ( create_event )
--
2.20.1

View File

@ -0,0 +1,73 @@
From b3f22ae85154f22532b66362a2399592d755ce33 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 15:41:01 -0500
Subject: [PATCH 09/83] ntdll: Implement NtSetEvent().
---
dlls/ntdll/esync.c | 21 +++++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/sync.c | 4 ++++
3 files changed, 26 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index ace2f4a45..62c56af75 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -282,6 +282,27 @@ NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
return ret;
}
+NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
+{
+ struct event *event = esync_get_object( handle );
+ static const uint64_t value = 1;
+
+ TRACE("%p.\n", handle);
+
+ if (!event) return STATUS_INVALID_HANDLE;
+
+ if (prev)
+ {
+ FIXME("Can't write previous value.\n");
+ *prev = 1;
+ }
+
+ if (write( event->obj.fd, &value, sizeof(value) ) == -1)
+ return FILE_GetNtStatus();
+
+ return STATUS_SUCCESS;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 32a259e39..ba1c89665 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -26,6 +26,7 @@ extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 6f0e72125..1e1e381b4 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -417,6 +417,10 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
{
NTSTATUS ret;
+
+ if (do_esync())
+ return esync_set_event( handle, prev_state );
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,73 @@
From 022deba562034611dccefaf2af504861e391fe7c Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 15:44:49 -0500
Subject: [PATCH 10/83] ntdll: Implement NtResetEvent().
---
dlls/ntdll/esync.c | 21 +++++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/sync.c | 4 ++++
3 files changed, 26 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 62c56af75..9e091819a 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -303,6 +303,27 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
+{
+ struct event *event = esync_get_object( handle );
+ static uint64_t value;
+
+ TRACE("%p.\n", handle);
+
+ if (!event) return STATUS_INVALID_HANDLE;
+
+ if (prev)
+ {
+ FIXME("Can't write previous value.\n");
+ *prev = 1;
+ }
+
+ /* we don't care about the return value */
+ read( event->obj.fd, &value, sizeof(value) );
+
+ return STATUS_SUCCESS;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index ba1c89665..8d2b4683e 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -27,6 +27,7 @@ extern NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev
extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 1e1e381b4..4be641eb5 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -438,6 +438,10 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
{
NTSTATUS ret;
+
+ if (do_esync())
+ return esync_reset_event( handle, prev_state );
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,76 @@
From 728fb6100bab81223baad2815878fccfe7fe17a6 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 15:47:16 -0500
Subject: [PATCH 11/83] ntdll: Implement NtPulseEvent().
---
dlls/ntdll/esync.c | 25 +++++++++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/sync.c | 3 +++
3 files changed, 29 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 9e091819a..1055e11ae 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -324,6 +324,31 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
+{
+ struct event *event = esync_get_object( handle );
+ static uint64_t value = 1;
+
+ TRACE("%p.\n", handle);
+
+ if (!event) return STATUS_INVALID_HANDLE;
+
+ if (prev)
+ {
+ FIXME("Can't write previous value.\n");
+ *prev = 1;
+ }
+
+ /* 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( event->obj.fd, &value, sizeof(value) ) == -1)
+ return FILE_GetNtStatus();
+ read( event->obj.fd, &value, sizeof(value) );
+
+ return STATUS_SUCCESS;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 8d2b4683e..551257fbc 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -28,6 +28,7 @@ extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 4be641eb5..74b50cbc7 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -474,6 +474,9 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
{
NTSTATUS ret;
+ if (do_esync())
+ return esync_pulse_event( handle, prev_state );
+
SERVER_START_REQ( event_op )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,46 @@
From ba99a79e4ed4be4525e14aec71eaf52421580c32 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 15:55:39 -0500
Subject: [PATCH 12/83] ntdll: Implement waiting on events.
More specifically, implement waiting on manual-reset events. Auto-reset events already worked.
---
dlls/ntdll/esync.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 1055e11ae..7a384dc61 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -471,11 +471,23 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
int64_t value;
ssize_t size;
- if ((size = read( fds[i].fd, &value, sizeof(value) )) == sizeof(value))
+ if (objs[i]->type == ESYNC_MANUAL_EVENT)
{
- /* We found our object. */
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- return i;
+ /* 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);
+ return i;
+ }
}
}
}
--
2.20.1

View File

@ -0,0 +1,665 @@
From c9b1a52208d85b3f35e7b842f0b930e9ff8d40e8 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 18:51:40 -0500
Subject: [PATCH 13/83] server: Add an object operation to grab the esync file
descriptor.
Split off to decrease patch size.
---
server/async.c | 2 ++
server/atom.c | 1 +
server/change.c | 1 +
server/clipboard.c | 1 +
server/completion.c | 1 +
server/console.c | 3 +++
server/debugger.c | 2 ++
server/device.c | 4 ++++
server/directory.c | 2 ++
server/esync.c | 1 +
server/event.c | 2 ++
server/fd.c | 4 ++++
server/file.c | 1 +
server/handle.c | 1 +
server/hook.c | 1 +
server/mailslot.c | 3 +++
server/mapping.c | 3 +++
server/mutex.c | 1 +
server/named_pipe.c | 5 +++++
server/object.h | 2 ++
server/process.c | 3 +++
server/queue.c | 2 ++
server/registry.c | 1 +
server/request.c | 1 +
server/semaphore.c | 1 +
server/serial.c | 1 +
server/signal.c | 1 +
server/snapshot.c | 1 +
server/sock.c | 2 ++
server/symlink.c | 1 +
server/thread.c | 2 ++
server/timer.c | 1 +
server/token.c | 1 +
server/winstation.c | 2 ++
34 files changed, 61 insertions(+)
diff --git a/server/async.c b/server/async.c
index b88dd1680..75989e5d3 100644
--- a/server/async.c
+++ b/server/async.c
@@ -69,6 +69,7 @@ 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 */
@@ -458,6 +459,7 @@ 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 */
diff --git a/server/atom.c b/server/atom.c
index 35db8e3e9..11b7ac532 100644
--- a/server/atom.c
+++ b/server/atom.c
@@ -80,6 +80,7 @@ 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 */
diff --git a/server/change.c b/server/change.c
index ec0a549aa..82e08359c 100644
--- a/server/change.c
+++ b/server/change.c
@@ -162,6 +162,7 @@ static const struct object_ops dir_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
default_fd_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
dir_get_fd, /* get_fd */
diff --git a/server/clipboard.c b/server/clipboard.c
index e6884dd75..673aabbd0 100644
--- a/server/clipboard.c
+++ b/server/clipboard.c
@@ -77,6 +77,7 @@ 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 */
diff --git a/server/completion.c b/server/completion.c
index aa60043bb..ed448b4d3 100644
--- a/server/completion.c
+++ b/server/completion.c
@@ -65,6 +65,7 @@ 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 */
diff --git a/server/console.c b/server/console.c
index 7d1fc5d26..059839e06 100644
--- a/server/console.c
+++ b/server/console.c
@@ -77,6 +77,7 @@ static const struct object_ops console_input_ops =
no_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 */
@@ -112,6 +113,7 @@ static const struct object_ops console_input_events_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
console_input_events_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -169,6 +171,7 @@ static const struct object_ops screen_buffer_ops =
no_add_queue, /* add_queue */
NULL, /* remove_queue */
NULL, /* signaled */
+ NULL, /* get_esync_fd */
NULL, /* satisfied */
no_signal, /* signal */
screen_buffer_get_fd, /* get_fd */
diff --git a/server/debugger.c b/server/debugger.c
index bff813a16..9a29ef41f 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -74,6 +74,7 @@ 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 */
@@ -101,6 +102,7 @@ static const struct object_ops debug_ctx_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
debug_ctx_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
diff --git a/server/device.c b/server/device.c
index 9e250a8a9..67505d99c 100644
--- a/server/device.c
+++ b/server/device.c
@@ -66,6 +66,7 @@ static const struct object_ops irp_call_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
irp_call_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -104,6 +105,7 @@ static const struct object_ops device_manager_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
device_manager_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -146,6 +148,7 @@ 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 */
@@ -192,6 +195,7 @@ 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 */
diff --git a/server/directory.c b/server/directory.c
index 4d0af8470..5972e5ae3 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -58,6 +58,7 @@ 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 */
@@ -96,6 +97,7 @@ 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 */
diff --git a/server/esync.c b/server/esync.c
index 147fb7724..f3a139da4 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -53,6 +53,7 @@ static const struct object_ops esync_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 */
diff --git a/server/event.c b/server/event.c
index 511a70564..f78c12d26 100644
--- a/server/event.c
+++ b/server/event.c
@@ -60,6 +60,7 @@ static const struct object_ops event_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
event_signaled, /* signaled */
+ NULL, /* get_esync_fd */
event_satisfied, /* satisfied */
event_signal, /* signal */
no_get_fd, /* get_fd */
@@ -94,6 +95,7 @@ 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 */
diff --git a/server/fd.c b/server/fd.c
index 309eb5ebf..a855c27d5 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -208,6 +208,7 @@ 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 */
@@ -248,6 +249,7 @@ 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 */
@@ -287,6 +289,7 @@ 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 */
@@ -328,6 +331,7 @@ 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 */
diff --git a/server/file.c b/server/file.c
index 9890f51e8..aa19b2b4c 100644
--- a/server/file.c
+++ b/server/file.c
@@ -94,6 +94,7 @@ 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 */
diff --git a/server/handle.c b/server/handle.c
index e40e9e95d..fc4e32f2f 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -123,6 +123,7 @@ 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 */
diff --git a/server/hook.c b/server/hook.c
index ec6be5824..f5d7639fd 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -81,6 +81,7 @@ 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 */
diff --git a/server/mailslot.c b/server/mailslot.c
index 9934884dc..a1ed2815c 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -79,6 +79,7 @@ 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 */
@@ -136,6 +137,7 @@ 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 */
@@ -194,6 +196,7 @@ 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 */
mailslot_device_get_fd, /* get_fd */
diff --git a/server/mapping.c b/server/mapping.c
index 0d0976a34..01e3f6760 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -91,6 +91,7 @@ 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 */
@@ -126,6 +127,7 @@ 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 */
@@ -182,6 +184,7 @@ 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 */
diff --git a/server/mutex.c b/server/mutex.c
index a7ac1759c..d1505c959 100644
--- a/server/mutex.c
+++ b/server/mutex.c
@@ -61,6 +61,7 @@ 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 */
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 695684b53..aa3a8b7b7 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -118,6 +118,7 @@ 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 */
@@ -160,6 +161,7 @@ static const struct object_ops pipe_server_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
default_fd_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
pipe_end_get_fd, /* get_fd */
@@ -202,6 +204,7 @@ static const struct object_ops pipe_client_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
default_fd_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
pipe_end_get_fd, /* get_fd */
@@ -248,6 +251,7 @@ 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 */
@@ -277,6 +281,7 @@ 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 */
diff --git a/server/object.h b/server/object.h
index a9aca35b2..592b76617 100644
--- a/server/object.h
+++ b/server/object.h
@@ -68,6 +68,8 @@ 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 *);
/* wait satisfied */
void (*satisfied)(struct object *,struct wait_queue_entry *);
/* signal an object */
diff --git a/server/process.c b/server/process.c
index edd41294e..a96972e6b 100644
--- a/server/process.c
+++ b/server/process.c
@@ -76,6 +76,7 @@ static const struct object_ops process_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
process_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -126,6 +127,7 @@ 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 */
@@ -170,6 +172,7 @@ 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 */
diff --git a/server/queue.c b/server/queue.c
index eb7b346f6..3817ce543 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -178,6 +178,7 @@ static const struct object_ops msg_queue_ops =
msg_queue_add_queue, /* add_queue */
msg_queue_remove_queue, /* remove_queue */
msg_queue_signaled, /* signaled */
+ NULL, /* get_esync_fd */
msg_queue_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -214,6 +215,7 @@ 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 */
diff --git a/server/registry.c b/server/registry.c
index 304fa1a11..85321d417 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -167,6 +167,7 @@ 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 */
diff --git a/server/request.c b/server/request.c
index 139d643e8..73878cf23 100644
--- a/server/request.c
+++ b/server/request.c
@@ -97,6 +97,7 @@ 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 */
diff --git a/server/semaphore.c b/server/semaphore.c
index 33615047e..9cab1db60 100644
--- a/server/semaphore.c
+++ b/server/semaphore.c
@@ -58,6 +58,7 @@ 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 */
diff --git a/server/serial.c b/server/serial.c
index 429247261..2848e1dc7 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -92,6 +92,7 @@ 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 */
diff --git a/server/signal.c b/server/signal.c
index c81f6b8e9..ca200394f 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -67,6 +67,7 @@ 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 */
diff --git a/server/snapshot.c b/server/snapshot.c
index 2b7438c00..7d0d742ee 100644
--- a/server/snapshot.c
+++ b/server/snapshot.c
@@ -61,6 +61,7 @@ static const struct object_ops snapshot_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 */
diff --git a/server/sock.c b/server/sock.c
index a8f8e0c38..d2c5795b6 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -146,6 +146,7 @@ static const struct object_ops sock_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
sock_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
sock_get_fd, /* get_fd */
@@ -995,6 +996,7 @@ static const struct object_ops ifchange_ops =
add_queue, /* add_queue */
NULL, /* remove_queue */
NULL, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
ifchange_get_fd, /* get_fd */
diff --git a/server/symlink.c b/server/symlink.c
index 9a8914881..df590ab7f 100644
--- a/server/symlink.c
+++ b/server/symlink.c
@@ -60,6 +60,7 @@ 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 */
diff --git a/server/thread.c b/server/thread.c
index ca976b29c..cf401f79b 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -110,6 +110,7 @@ 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 */
@@ -143,6 +144,7 @@ static const struct object_ops thread_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
thread_signaled, /* signaled */
+ NULL, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
diff --git a/server/timer.c b/server/timer.c
index 7a2057d1b..109aa0ff9 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -65,6 +65,7 @@ static const struct object_ops timer_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
timer_signaled, /* signaled */
+ NULL, /* get_esync_fd */
timer_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
diff --git a/server/token.c b/server/token.c
index eb0d73992..42ac6de6b 100644
--- a/server/token.c
+++ b/server/token.c
@@ -152,6 +152,7 @@ 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 */
diff --git a/server/winstation.c b/server/winstation.c
index 55600fbe9..9e4238292 100644
--- a/server/winstation.c
+++ b/server/winstation.c
@@ -65,6 +65,7 @@ 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 +90,7 @@ 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 */
--
2.20.1

View File

@ -0,0 +1,64 @@
From 74d7000943e1037303294332b415b0b4b5e8291c Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 18:55:49 -0500
Subject: [PATCH 14/83] server: Add a request to get the eventfd file
descriptor associated with a waitable handle.
---
server/esync.c | 28 ++++++++++++++++++++++++++++
server/protocol.def | 6 ++++++
2 files changed, 34 insertions(+)
diff --git a/server/esync.c b/server/esync.c
index f3a139da4..351da1a7c 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -136,3 +136,31 @@ DECL_HANDLER(create_esync)
if (root) release_object( root );
}
+
+/* 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;
+ 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 );
+ 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 );
+}
diff --git a/server/protocol.def b/server/protocol.def
index a56f098ab..5f2d77bc7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4051,3 +4051,9 @@ struct handle_info
@REPLY
obj_handle_t handle; /* handle to the object */
@END
+
+/* Retrieve the esync fd for an object. */
+@REQ(get_esync_fd)
+ obj_handle_t handle; /* handle to the object */
+@REPLY
+@END
--
2.20.1

View File

@ -0,0 +1,178 @@
From 4473e26d9c3177ee7a42f023fcbe48edc672f9bc Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 18:57:12 -0500
Subject: [PATCH 15/83] server: Create eventfd file descriptors for process
objects.
---
server/esync.c | 33 +++++++++++++++++++++++++++++++++
server/esync.h | 22 ++++++++++++++++++++++
server/process.c | 17 ++++++++++++++++-
server/process.h | 1 +
4 files changed, 72 insertions(+), 1 deletion(-)
create mode 100644 server/esync.h
diff --git a/server/esync.c b/server/esync.c
index 351da1a7c..da26d27cb 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -35,6 +35,21 @@
#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") != NULL);
+
+ return do_esync_cached;
+#else
+ return 0;
+#endif
+}
struct esync
{
@@ -112,6 +127,24 @@ struct esync *create_esync( struct object *root, const struct unicode_str *name,
#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
+}
+
DECL_HANDLER(create_esync)
{
struct esync *esync;
diff --git a/server/esync.h b/server/esync.h
new file mode 100644
index 000000000..f93535b7b
--- /dev/null
+++ b/server/esync.h
@@ -0,0 +1,22 @@
+/*
+ * 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);
+int esync_create_fd( int initval, int flags );
diff --git a/server/process.c b/server/process.c
index a96972e6b..0bb220dba 100644
--- a/server/process.c
+++ b/server/process.c
@@ -48,6 +48,7 @@
#include "request.h"
#include "user.h"
#include "security.h"
+#include "esync.h"
/* process structure */
@@ -66,6 +67,7 @@ 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 void process_destroy( struct object *obj );
+static int process_get_esync_fd( struct object *obj );
static void terminate_process( struct process *process, struct thread *skip, int exit_code );
static const struct object_ops process_ops =
@@ -76,7 +78,7 @@ static const struct object_ops process_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
process_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ process_get_esync_fd, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -529,6 +531,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all,
process->trace_data = 0;
process->rawinput_mouse = NULL;
process->rawinput_kbd = NULL;
+ process->esync_fd = -1;
list_init( &process->thread_list );
list_init( &process->locks );
list_init( &process->asyncs );
@@ -572,6 +575,9 @@ struct process *create_process( int fd, struct process *parent, int inherit_all,
}
if (!process->handles || !process->token) goto error;
+ if (do_esync())
+ process->esync_fd = esync_create_fd( 0, 0 );
+
set_fd_events( process->msg_fd, POLLIN ); /* start listening to events */
return process;
@@ -620,6 +626,9 @@ static void process_destroy( struct object *obj )
if (process->id) free_ptid( process->id );
if (process->token) release_object( process->token );
free( process->dir_cache );
+
+ if (do_esync())
+ close( process->esync_fd );
}
/* dump a process on stdout for debugging purposes */
@@ -643,6 +652,12 @@ 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 )
+{
+ struct process *process = (struct process *)obj;
+ return process->esync_fd;
+}
+
static unsigned int process_map_access( struct object *obj, unsigned int access )
{
if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
diff --git a/server/process.h b/server/process.h
index ea2809194..f8fd12833 100644
--- a/server/process.h
+++ b/server/process.h
@@ -96,6 +96,7 @@ struct process
struct list rawinput_devices;/* list of registered rawinput devices */
const struct rawinput_device *rawinput_mouse; /* rawinput mouse device, if any */
const struct rawinput_device *rawinput_kbd; /* rawinput keyboard device, if any */
+ int esync_fd; /* esync file descriptor (signaled on exit) */
};
struct process_snapshot
--
2.20.1

View File

@ -0,0 +1,192 @@
From 4f6cb6b5955df87982491aed1fd23e18051d095a Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 19:25:05 -0500
Subject: [PATCH 16/83] ntdll, server: Implement waiting on server-bound
objects.
The code here is sort of self-explanatory, but since I split it up over
several patches I'll provide a quick explanation. The basic principle is
that we can create an eventfd descriptor for any synchronizable handle, and
signal it on the server side whenever a wakeup would be triggered. This means
not only that we can wait simultaneously on esync primitives and on other
primitives, but that we can do it all in "user-mode", i.e. without having to
make a server call.
With this patch we break waiting on svcctl.exe.
---
dlls/ntdll/esync.c | 68 +++++++++++++++++++++++++++++++++++++++++++---
server/esync.c | 16 +++++++++++
server/esync.h | 1 +
server/thread.c | 4 +++
4 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 7a384dc61..23b03b23b 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -79,6 +79,7 @@ enum esync_type
ESYNC_SEMAPHORE = 1,
ESYNC_AUTO_EVENT,
ESYNC_MANUAL_EVENT,
+ ESYNC_MANUAL_SERVER,
};
struct esync
@@ -150,6 +151,63 @@ static void *esync_get_object( HANDLE handle )
return esync_list[entry][idx];
}
+/* Gets a waitable 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_waitable_object( HANDLE handle, struct esync **obj )
+{
+ obj_handle_t fd_handle;
+ struct esync *esync;
+ sigset_t sigset;
+ NTSTATUS ret;
+ int fd;
+
+ if ((*obj = esync_get_object( handle ))) return STATUS_SUCCESS;
+
+ /* We need to try grabbing it from the server. */
+ server_enter_uninterrupted_section( &fd_cache_section, &sigset );
+ if (!(esync = esync_get_object( handle )))
+ {
+ SERVER_START_REQ( get_esync_fd )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ if (!(ret = wine_server_call( req )))
+ {
+ fd = receive_fd( &fd_handle );
+ assert( wine_server_ptr_handle(fd_handle) == handle );
+ }
+ }
+ SERVER_END_REQ;
+ }
+ server_leave_uninterrupted_section( &fd_cache_section, &sigset );
+
+ if (esync)
+ {
+ /* We managed to grab it while in the CS; return it. */
+ *obj = esync;
+ 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);
+
+ esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
+ esync->fd = fd;
+ esync->type = ESYNC_MANUAL_SERVER;
+
+ add_to_list( handle, esync );
+
+ *obj = esync;
+ return ret;
+}
+
NTSTATUS esync_close( HANDLE handle )
{
UINT_PTR entry, idx = handle_to_index( handle, &entry );
@@ -410,11 +468,13 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
for (i = 0; i < count; i++)
{
- objs[i] = esync_get_object( handles[i] );
- if (objs[i])
+ ret = get_waitable_object( handles[i], &objs[i] );
+ if (ret == STATUS_SUCCESS)
has_esync = 1;
- else
+ else if (ret == STATUS_NOT_IMPLEMENTED)
has_server = 1;
+ else
+ return ret;
}
if (has_esync && has_server)
@@ -471,7 +531,7 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
int64_t value;
ssize_t size;
- if (objs[i]->type == ESYNC_MANUAL_EVENT)
+ if (objs[i]->type == ESYNC_MANUAL_EVENT || objs[i]->type == ESYNC_MANUAL_SERVER)
{
/* Don't grab the object, just check if it's signaled. */
if (fds[i].revents & POLLIN)
diff --git a/server/esync.c b/server/esync.c
index da26d27cb..96bf6a57f 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "wine/port.h"
+#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef HAVE_SYS_EVENTFD_H
@@ -145,6 +146,21 @@ int esync_create_fd( int initval, int flags )
#endif
}
+/* Wake up a server-side esync object. */
+void esync_wake_up( struct object *obj )
+{
+ static const uint64_t value = 1;
+ int fd;
+
+ if (obj->ops->get_esync_fd)
+ {
+ fd = obj->ops->get_esync_fd( obj );
+
+ if (write( fd, &value, sizeof(value) ) == -1)
+ perror( "esync: write" );
+ }
+}
+
DECL_HANDLER(create_esync)
{
struct esync *esync;
diff --git a/server/esync.h b/server/esync.h
index f93535b7b..bbfe0aef8 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -20,3 +20,4 @@
extern int do_esync(void);
int esync_create_fd( int initval, int flags );
+void esync_wake_up( struct object *obj );
diff --git a/server/thread.c b/server/thread.c
index cf401f79b..ae00c89da 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -51,6 +51,7 @@
#include "request.h"
#include "user.h"
#include "security.h"
+#include "esync.h"
#ifdef __i386__
@@ -940,6 +941,9 @@ 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 );
--
2.20.1

View File

@ -0,0 +1,144 @@
From da31b19b8edb1c270fe3735ac2f5d54132c3007c Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 21:01:24 -0500
Subject: [PATCH 17/83] server: Create eventfd file descriptors for event
objects.
We still need this, since there are some events which the server signals.
This lets system processes shut down.
---
server/esync.c | 8 ++++++++
server/esync.h | 1 +
server/event.c | 28 ++++++++++++++++++++++++++--
3 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/server/esync.c b/server/esync.c
index 96bf6a57f..406c1a557 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -161,6 +161,14 @@ void esync_wake_up( struct object *obj )
}
}
+void esync_clear( int fd )
+{
+ uint64_t value;
+
+ /* we don't care about the return value */
+ read( fd, &value, sizeof(value) );
+}
+
DECL_HANDLER(create_esync)
{
struct esync *esync;
diff --git a/server/esync.h b/server/esync.h
index bbfe0aef8..054a77190 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -21,3 +21,4 @@
extern int do_esync(void);
int esync_create_fd( int initval, int flags );
void esync_wake_up( struct object *obj );
+void esync_clear( int fd );
diff --git a/server/event.c b/server/event.c
index f78c12d26..12724c581 100644
--- a/server/event.c
+++ b/server/event.c
@@ -35,6 +35,7 @@
#include "thread.h"
#include "request.h"
#include "security.h"
+#include "esync.h"
struct event
{
@@ -42,15 +43,18 @@ 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 */
};
static void event_dump( struct object *obj, int verbose );
static struct object_type *event_get_type( struct object *obj );
static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
+static int event_get_esync_fd( struct object *obj );
static unsigned int event_map_access( struct object *obj, unsigned int access );
static int event_signal( struct object *obj, unsigned int access);
static struct list *event_get_kernel_obj_list( struct object *obj );
+static void event_destroy( struct object *obj );
static const struct object_ops event_ops =
{
@@ -60,7 +64,7 @@ static const struct object_ops event_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
event_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ event_get_esync_fd, /* get_esync_fd */
event_satisfied, /* satisfied */
event_signal, /* signal */
no_get_fd, /* get_fd */
@@ -73,7 +77,7 @@ static const struct object_ops event_ops =
no_open_file, /* open_file */
event_get_kernel_obj_list, /* get_kernel_obj_list */
no_close_handle, /* close_handle */
- no_destroy /* destroy */
+ event_destroy /* destroy */
};
@@ -126,6 +130,9 @@ 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 );
}
}
return event;
@@ -154,6 +161,9 @@ void set_event( struct event *event )
void reset_event( struct event *event )
{
event->signaled = 0;
+
+ if (do_esync())
+ esync_clear( event->esync_fd );
}
static void event_dump( struct object *obj, int verbose )
@@ -177,6 +187,12 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry )
return event->signaled;
}
+static int event_get_esync_fd( struct object *obj )
+{
+ struct event *event = (struct event *)obj;
+ return event->esync_fd;
+}
+
static void event_satisfied( struct object *obj, struct wait_queue_entry *entry )
{
struct event *event = (struct event *)obj;
@@ -214,6 +230,14 @@ static struct list *event_get_kernel_obj_list( struct object *obj )
return &event->kernel_object;
}
+static void event_destroy( struct object *obj )
+{
+ struct event *event = (struct event *)obj;
+
+ if (do_esync())
+ close( event->esync_fd );
+}
+
struct keyed_event *create_keyed_event( struct object *root, const struct unicode_str *name,
unsigned int attr, const struct security_descriptor *sd )
{
--
2.20.1

View File

@ -0,0 +1,117 @@
From 06935535827e6ce33ce77ae9c17165d4623b22ec Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 21:43:37 -0500
Subject: [PATCH 18/83] server: Allow (re)setting esync events on the server
side.
Some server calls pass an event handle, most notably asyncs. We need to be
able to handle these correctly. Accordingly we pass them along to esync if
it turns out the underlying object is actually an esync object.
In an ideal world we'd just convert all instances of events on the server
side to use esyncs instead. But we want to keep esync perfectly configurable,
so this is how we do it.
---
server/esync.c | 22 +++++++++++++++++++++-
server/esync.h | 6 ++++++
server/event.c | 15 +++++++++++++++
3 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/server/esync.c b/server/esync.c
index 406c1a557..4f7ff6bda 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -61,7 +61,7 @@ struct esync
static void esync_dump( struct object *obj, int verbose );
static void esync_destroy( struct object *obj );
-static const struct object_ops esync_ops =
+const struct object_ops esync_ops =
{
sizeof(struct esync), /* size */
esync_dump, /* dump */
@@ -169,6 +169,26 @@ void esync_clear( int fd )
read( fd, &value, sizeof(value) );
}
+/* Server-side event support. */
+void esync_set_event( struct esync *esync )
+{
+ static const uint64_t value = 1;
+
+ assert( esync->obj.ops == &esync_ops );
+ if (write( esync->fd, &value, sizeof(value) ) == -1)
+ perror( "esync: write" );
+}
+
+void esync_reset_event( struct esync *esync )
+{
+ static uint64_t value = 1;
+
+ assert( esync->obj.ops == &esync_ops );
+
+ /* we don't care about the return value */
+ read( esync->fd, &value, sizeof(value) );
+}
+
DECL_HANDLER(create_esync)
{
struct esync *esync;
diff --git a/server/esync.h b/server/esync.h
index 054a77190..2687c72e4 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -22,3 +22,9 @@ extern int do_esync(void);
int esync_create_fd( int initval, int flags );
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 );
diff --git a/server/event.c b/server/event.c
index 12724c581..62d8bf7d3 100644
--- a/server/event.c
+++ b/server/event.c
@@ -140,6 +140,10 @@ 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 );
}
@@ -153,6 +157,12 @@ 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 );
@@ -160,6 +170,11 @@ void set_event( struct event *event )
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())
--
2.20.1

View File

@ -0,0 +1,69 @@
From fe2e94200cfb6a88e82b9261f2d8a05a6fae0cbe Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 21:58:37 -0500
Subject: [PATCH 19/83] ntdll: Try again if poll() returns EINTR.
I originally had this return STATUS_USER_APC, but that isn't correct. The
server code here is a bit confusing, but only the thread that waits *during*
the suspend should receive STATUS_USER_APC (and I imagine that it really
should receive STATUS_KERNEL_APC instead). The thread that is suspended
should just keep on waiting.
Besides, we could be suspended for reasons other than to deliver a system
APC.
---
dlls/ntdll/esync.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 23b03b23b..0655ab779 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -423,22 +423,32 @@ static LONGLONG update_timeout( ULONGLONG end )
static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end )
{
- if (end)
+ int ret;
+
+ do
{
- LONGLONG timeleft = update_timeout( *end );
+ 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;
- return ppoll( fds, nfds, &tmo_p, NULL );
+ /* 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
- return poll( fds, nfds, timeleft / TICKSPERMSEC );
+ ret = poll( fds, nfds, timeleft / TICKSPERMSEC );
#endif
- }
- else
- return poll( fds, nfds, -1 );
+ }
+ 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;
}
/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we
--
2.20.1

View File

@ -0,0 +1,88 @@
From 69c158267facd8919329e71e082e872031ee4054 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 22:04:29 -0500
Subject: [PATCH 20/83] server: Create eventfd file descriptors for thread
objects.
---
server/thread.c | 16 +++++++++++++++-
server/thread.h | 1 +
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/server/thread.c b/server/thread.c
index ae00c89da..e82d71f67 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -133,6 +133,7 @@ static const struct object_ops thread_apc_ops =
static void dump_thread( struct object *obj, int verbose );
static struct object_type *thread_get_type( struct object *obj );
static int thread_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int thread_get_esync_fd( struct object *obj );
static unsigned int thread_map_access( struct object *obj, unsigned int access );
static void thread_poll_event( struct fd *fd, int event );
static void destroy_thread( struct object *obj );
@@ -145,7 +146,7 @@ static const struct object_ops thread_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
thread_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ thread_get_esync_fd, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -185,6 +186,7 @@ static inline void init_thread_structure( struct thread *thread )
thread->suspend_context = NULL;
thread->teb = 0;
thread->entry_point = 0;
+ thread->esync_fd = -1;
thread->debug_ctx = NULL;
thread->debug_event = NULL;
thread->debug_break = 0;
@@ -292,6 +294,9 @@ struct thread *create_thread( int fd, struct process *process, const struct secu
return NULL;
}
+ if (do_esync())
+ thread->esync_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;
@@ -364,6 +369,9 @@ static void destroy_thread( struct object *obj )
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 );
}
/* dump a thread on stdout for debugging purposes */
@@ -388,6 +396,12 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
return mythread->state == TERMINATED && !mythread->exit_poll;
}
+static int thread_get_esync_fd( struct object *obj )
+{
+ struct thread *thread = (struct thread *)obj;
+ return thread->esync_fd;
+}
+
static unsigned int thread_map_access( struct object *obj, unsigned int access )
{
if (access & GENERIC_READ) access |= STANDARD_RIGHTS_READ | THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT;
diff --git a/server/thread.h b/server/thread.h
index bfd818ce2..4913577f7 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -54,6 +54,7 @@ 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) */
struct debug_ctx *debug_ctx; /* debugger context if this thread is a debugger */
struct debug_event *debug_event; /* debug event being sent to debugger */
int debug_break; /* debug breakpoint pending? */
--
2.20.1

View File

@ -0,0 +1,49 @@
From 57dfceffa275576dd721d18702a231d495e8a611 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 23:30:17 -0500
Subject: [PATCH 21/83] rpcrt4: Avoid closing the server thread handle while it
is being waited on.
This, or something like this, should go upstream. This is invalid behaviour.
---
dlls/rpcrt4/rpc_server.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/rpcrt4/rpc_server.c b/dlls/rpcrt4/rpc_server.c
index 91de3b95f..af79dc56c 100644
--- a/dlls/rpcrt4/rpc_server.c
+++ b/dlls/rpcrt4/rpc_server.c
@@ -702,10 +702,6 @@ static DWORD CALLBACK RPCRT4_server_thread(LPVOID the_arg)
}
LeaveCriticalSection(&cps->cs);
- EnterCriticalSection(&listen_cs);
- CloseHandle(cps->server_thread);
- cps->server_thread = NULL;
- LeaveCriticalSection(&listen_cs);
TRACE("done\n");
return 0;
}
@@ -1573,7 +1569,10 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void )
LIST_FOR_EACH_ENTRY(protseq, &protseqs, RpcServerProtseq, entry)
{
if ((wait_thread = protseq->server_thread))
+ {
+ protseq->server_thread = NULL;
break;
+ }
}
LeaveCriticalSection(&server_cs);
if (!wait_thread)
@@ -1582,6 +1581,7 @@ RPC_STATUS WINAPI RpcMgmtWaitServerListen( void )
TRACE("waiting for thread %u\n", GetThreadId(wait_thread));
LeaveCriticalSection(&listen_cs);
WaitForSingleObject(wait_thread, INFINITE);
+ CloseHandle(wait_thread);
EnterCriticalSection(&listen_cs);
}
if (listen_done_event == event)
--
2.20.1

View File

@ -0,0 +1,114 @@
From 6e75c09ce0d31bd7c8aec3129d0f9d2fe711b412 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 8 Jun 2018 23:41:54 -0500
Subject: [PATCH 22/83] server: Create eventfd file descriptors for message
queues.
Note that we don't have to worry about races here, because a thread can only
check its own queue.
---
server/queue.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/server/queue.c b/server/queue.c
index 3817ce543..0ea95291c 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -43,6 +43,7 @@
#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))
@@ -124,6 +125,7 @@ struct msg_queue
struct object obj; /* object header */
struct thread *thread; /* reference to the thread owning the queue */
struct fd *fd; /* optional file descriptor to poll */
+ int esync_fd; /* esync file descriptor (signalled on message) */
unsigned int wake_bits; /* wakeup bits */
unsigned int wake_mask; /* wakeup mask */
unsigned int changed_bits; /* changed wakeup bits */
@@ -163,6 +165,7 @@ static void msg_queue_dump( struct object *obj, int verbose );
static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry );
static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int msg_queue_get_esync_fd( struct object *obj );
static void msg_queue_satisfied( struct object *obj, struct wait_queue_entry *entry );
static void msg_queue_destroy( struct object *obj );
static void msg_queue_poll_event( struct fd *fd, int event );
@@ -178,7 +181,7 @@ static const struct object_ops msg_queue_ops =
msg_queue_add_queue, /* add_queue */
msg_queue_remove_queue, /* remove_queue */
msg_queue_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ msg_queue_get_esync_fd, /* get_esync_fd */
msg_queue_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -317,6 +320,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
if ((queue = alloc_object( &msg_queue_ops )))
{
queue->fd = NULL;
+ queue->esync_fd = -1;
queue->thread = thread;
queue->wake_bits = 0;
queue->wake_mask = 0;
@@ -341,6 +345,9 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
list_init( &queue->expired_timers );
for (i = 0; i < NB_MSG_KINDS; i++) list_init( &queue->msg_list[i] );
+ if (do_esync())
+ queue->esync_fd = esync_create_fd( 0, 0 );
+
thread->queue = queue;
}
if (new_input)
@@ -517,6 +524,9 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits
queue->wake_bits &= ~bits;
queue->changed_bits &= ~bits;
update_shm_queue_bits( queue );
+
+ if (do_esync() && !is_signaled( queue ))
+ esync_clear( queue->esync_fd );
}
/* check whether msg is a keyboard message */
@@ -1030,6 +1040,12 @@ 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 )
+{
+ struct msg_queue *queue = (struct msg_queue *)obj;
+ 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;
@@ -1075,6 +1091,9 @@ static void msg_queue_destroy( struct object *obj )
release_object( queue->input );
if (queue->hooks) release_object( queue->hooks );
if (queue->fd) release_object( queue->fd );
+
+ if (do_esync())
+ close( queue->esync_fd );
}
static void msg_queue_poll_event( struct fd *fd, int event )
@@ -2350,6 +2369,9 @@ DECL_HANDLER(get_queue_status)
reply->wake_bits = queue->wake_bits;
reply->changed_bits = queue->changed_bits;
queue->changed_bits &= ~req->clear_bits;
+
+ if (do_esync() && !is_signaled( queue ))
+ esync_clear( queue->esync_fd );
}
else reply->wake_bits = reply->changed_bits = 0;
}
--
2.20.1

View File

@ -0,0 +1,147 @@
From 86714f32b54c3135e5626f66f65ae5daf946f744 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 12 Jun 2018 10:01:08 -0500
Subject: [PATCH 23/83] ntdll, wineandroid.drv, winemac.drv, winex11.drv: Store
the thread's queue fd in ntdll.
I originally had esync grab it from the server in order to avoid touching the USER drivers, but this doesn't really work since we might perform a MsgWait before ever doing any operations which might initialize the USER driver's thread data. Doing it this way also has the advantage that we won't use extra file descriptors.
In any case I haven't bothered to guard this code with do_esync(); it'd require adding that function to each USER driver and it's pretty obvious it's not going to affect anything if esync is disabled.
---
dlls/ntdll/esync.c | 6 ++++++
dlls/ntdll/ntdll.spec | 2 ++
dlls/ntdll/ntdll_misc.h | 1 +
dlls/ntdll/thread.c | 2 ++
dlls/wineandroid.drv/window.c | 3 +++
dlls/winemac.drv/macdrv_main.c | 3 +++
dlls/winex11.drv/x11drv_main.c | 3 +++
7 files changed, 20 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 0655ab779..210556fc2 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -74,6 +74,12 @@ int do_esync(void)
#endif
}
+/* Entry point for drivers to set queue fd. */
+void __wine_esync_set_queue_fd( int fd )
+{
+ ntdll_get_thread_data()->esync_queue_fd = fd;
+}
+
enum esync_type
{
ESYNC_SEMAPHORE = 1,
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 74cebfc42..343ab66a5 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1498,6 +1498,8 @@
# All functions must be prefixed with '__wine_' (for internal functions)
# or 'wine_' (for user-visible functions) to avoid namespace conflicts.
+@ cdecl __wine_esync_set_queue_fd(long)
+
# Server interface
@ cdecl -norelay wine_server_call(ptr)
@ cdecl wine_server_close_fds_by_type(long)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index dad0996c4..e2ef7d9b8 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -280,6 +280,7 @@ struct debug_info
struct ntdll_thread_data
{
struct debug_info *debug_info; /* info for debugstr functions */
+ int esync_queue_fd;/* fd to wait on for driver events */
void *start_stack; /* stack for thread startup */
int request_fd; /* fd for sending server requests */
int reply_fd; /* fd for receiving server replies */
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index a1ff7c1dd..87134db09 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -326,6 +326,7 @@ void thread_init(void)
thread_data->reply_fd = -1;
thread_data->wait_fd[0] = -1;
thread_data->wait_fd[1] = -1;
+ thread_data->esync_queue_fd = -1;
signal_init_thread( teb );
virtual_init_threading();
@@ -694,6 +695,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle_ptr, ACCESS_MASK access, OBJECT
thread_data->wait_fd[0] = -1;
thread_data->wait_fd[1] = -1;
thread_data->start_stack = (char *)teb->Tib.StackBase;
+ thread_data->esync_queue_fd = -1;
pthread_attr_init( &pthread_attr );
pthread_attr_setstack( &pthread_attr, teb->DeallocationStack,
diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c
index eb05aaf28..2fc258dfd 100644
--- a/dlls/wineandroid.drv/window.c
+++ b/dlls/wineandroid.drv/window.c
@@ -364,6 +364,8 @@ jboolean motion_event( JNIEnv *env, jobject obj, jint win, jint action, jint x,
}
+extern void __wine_esync_set_queue_fd( int fd );
+
/***********************************************************************
* init_event_queue
*/
@@ -377,6 +379,7 @@ static void init_event_queue(void)
ERR( "could not create data\n" );
ExitProcess(1);
}
+ __wine_esync_set_queue_fd( event_pipe[0] );
if (wine_server_fd_to_handle( event_pipe[0], GENERIC_READ | SYNCHRONIZE, 0, &handle ))
{
ERR( "Can't allocate handle for event fd\n" );
diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c
index 544d448f9..5dfa54966 100644
--- a/dlls/winemac.drv/macdrv_main.c
+++ b/dlls/winemac.drv/macdrv_main.c
@@ -321,6 +321,7 @@ void CDECL macdrv_ThreadDetach(void)
}
}
+extern void __wine_esync_set_queue_fd( int fd );
/***********************************************************************
* set_queue_display_fd
@@ -332,6 +333,8 @@ static void set_queue_display_fd(int fd)
HANDLE handle;
int ret;
+ __wine_esync_set_queue_fd(fd);
+
if (wine_server_fd_to_handle(fd, GENERIC_READ | SYNCHRONIZE, 0, &handle))
{
MESSAGE("macdrv: Can't allocate handle for event queue fd\n");
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index 40cd8ca29..18c98e3ec 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -624,6 +624,7 @@ void CDECL X11DRV_ThreadDetach(void)
}
}
+extern void __wine_esync_set_queue_fd( int fd );
/* store the display fd into the message queue */
static void set_queue_display_fd( Display *display )
@@ -631,6 +632,8 @@ static void set_queue_display_fd( Display *display )
HANDLE handle;
int ret;
+ __wine_esync_set_queue_fd( ConnectionNumber(display) );
+
if (wine_server_fd_to_handle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE, 0, &handle ))
{
MESSAGE( "x11drv: Can't allocate handle for display fd\n" );
--
2.20.1

View File

@ -0,0 +1,312 @@
From e3a723494fcecb85cebeeb8557aa21cf3f30f0b1 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 14:44:54 -0500
Subject: [PATCH 24/83] server, ntdll: Also wait on the queue fd when waiting
for driver events.
Normally the server handles this, by polling on the fd during its main loop.
The problem there is that the server only polls when the thread is waiting
for messages. We want to replicate that behaviour, otherwise wineserver spins
forever trying to wake up a thread that just doesn't care.
With this patch, I'm finally able to interact with winecfg. Next step is to
fix the 'drives' tab.
---
dlls/ntdll/esync.c | 44 ++++++++++++++++++++++++++++++++------------
server/esync.c | 7 +++++--
server/event.c | 5 +++--
server/object.h | 2 +-
server/process.c | 5 +++--
server/protocol.def | 10 ++++++++++
server/queue.c | 5 +++--
server/thread.c | 5 +++--
8 files changed, 60 insertions(+), 23 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 210556fc2..4d708c6df 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -80,17 +80,9 @@ void __wine_esync_set_queue_fd( int fd )
ntdll_get_thread_data()->esync_queue_fd = fd;
}
-enum esync_type
-{
- ESYNC_SEMAPHORE = 1,
- ESYNC_AUTO_EVENT,
- ESYNC_MANUAL_EVENT,
- ESYNC_MANUAL_SERVER,
-};
-
struct esync
{
- enum esync_type type;
+ enum esync_type type; /* defined in protocol.def */
int fd;
};
@@ -164,6 +156,7 @@ static void *esync_get_object( HANDLE handle )
static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
{
obj_handle_t fd_handle;
+ enum esync_type type;
struct esync *esync;
sigset_t sigset;
NTSTATUS ret;
@@ -180,6 +173,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
req->handle = wine_server_obj_handle( handle );
if (!(ret = wine_server_call( req )))
{
+ type = reply->type;
fd = receive_fd( &fd_handle );
assert( wine_server_ptr_handle(fd_handle) == handle );
}
@@ -206,7 +200,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
esync->fd = fd;
- esync->type = ESYNC_MANUAL_SERVER;
+ esync->type = type;
add_to_list( handle, esync );
@@ -463,8 +457,10 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
struct esync *objs[MAXIMUM_WAIT_OBJECTS];
- struct pollfd fds[MAXIMUM_WAIT_OBJECTS];
+ struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1];
int has_esync = 0, has_server = 0;
+ DWORD pollcount = count;
+ BOOL msgwait = FALSE;
LONGLONG timeleft;
LARGE_INTEGER now;
ULONGLONG end;
@@ -493,6 +489,15 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
return ret;
}
+ if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE)
+ {
+ /* Last object in the list is a queue, which means someone is using
+ * MsgWaitForMultipleObjects(). We have to wait not only for the server
+ * fd (signaled on send_message, etc.) but also the USER driver's fd
+ * (signaled on e.g. X11 events.) */
+ msgwait = TRUE;
+ }
+
if (has_esync && has_server)
{
FIXME("Can't wait on esync and server objects at the same time!\n");
@@ -510,6 +515,9 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
for (i = 0; i < count; i++)
DPRINTF(" %p", handles[i]);
+ if (msgwait)
+ DPRINTF(" or driver events (fd %d)", ntdll_get_thread_data()->esync_queue_fd);
+
if (!timeout)
DPRINTF(", timeout = INFINITE.\n");
else
@@ -527,10 +535,16 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
fds[i].fd = objs[i] ? objs[i]->fd : -1;
fds[i].events = POLLIN;
}
+ if (msgwait)
+ {
+ fds[count].fd = ntdll_get_thread_data()->esync_queue_fd;
+ fds[count].events = POLLIN;
+ pollcount++;
+ }
while (1)
{
- ret = do_poll( fds, count, timeout ? &end : NULL );
+ ret = do_poll( fds, pollcount, timeout ? &end : NULL );
if (ret > 0)
{
/* Find out which object triggered the wait. */
@@ -568,6 +582,12 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
}
}
+ if (msgwait && (fds[count].revents & POLLIN))
+ {
+ TRACE("Woken up by driver events.\n");
+ return count - 1;
+ }
+
/* If we got here, someone else stole (or reset, etc.) whatever
* we were waiting for. So keep waiting. */
NtQuerySystemTime( &now );
diff --git a/server/esync.c b/server/esync.c
index 4f7ff6bda..1f8c0d516 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -150,11 +150,12 @@ int esync_create_fd( int initval, int flags )
void esync_wake_up( struct object *obj )
{
static const uint64_t value = 1;
+ enum esync_type dummy;
int fd;
if (obj->ops->get_esync_fd)
{
- fd = obj->ops->get_esync_fd( obj );
+ fd = obj->ops->get_esync_fd( obj, &dummy );
if (write( fd, &value, sizeof(value) ) == -1)
perror( "esync: write" );
@@ -219,6 +220,7 @@ DECL_HANDLER(create_esync)
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 )))
@@ -226,7 +228,8 @@ DECL_HANDLER(get_esync_fd)
if (obj->ops->get_esync_fd)
{
- fd = obj->ops->get_esync_fd( obj );
+ fd = obj->ops->get_esync_fd( obj, &type );
+ reply->type = type;
send_client_fd( current->process, fd, req->handle );
}
else
diff --git a/server/event.c b/server/event.c
index 62d8bf7d3..9e0bf8cbf 100644
--- a/server/event.c
+++ b/server/event.c
@@ -50,7 +50,7 @@ static void event_dump( struct object *obj, int verbose );
static struct object_type *event_get_type( struct object *obj );
static int event_signaled( struct object *obj, struct wait_queue_entry *entry );
static void event_satisfied( struct object *obj, struct wait_queue_entry *entry );
-static int event_get_esync_fd( struct object *obj );
+static int event_get_esync_fd( struct object *obj, enum esync_type *type );
static unsigned int event_map_access( struct object *obj, unsigned int access );
static int event_signal( struct object *obj, unsigned int access);
static struct list *event_get_kernel_obj_list( struct object *obj );
@@ -202,9 +202,10 @@ static int event_signaled( struct object *obj, struct wait_queue_entry *entry )
return event->signaled;
}
-static int event_get_esync_fd( struct object *obj )
+static int event_get_esync_fd( struct object *obj, enum esync_type *type )
{
struct event *event = (struct event *)obj;
+ *type = ESYNC_MANUAL_SERVER; /* all server-created events are manual-reset */
return event->esync_fd;
}
diff --git a/server/object.h b/server/object.h
index 592b76617..23dcdafa4 100644
--- a/server/object.h
+++ b/server/object.h
@@ -69,7 +69,7 @@ struct object_ops
/* is object signaled? */
int (*signaled)(struct object *,struct wait_queue_entry *);
/* return the esync fd for this object */
- int (*get_esync_fd)(struct object *);
+ int (*get_esync_fd)(struct object *, enum esync_type *type);
/* wait satisfied */
void (*satisfied)(struct object *,struct wait_queue_entry *);
/* signal an object */
diff --git a/server/process.c b/server/process.c
index 0bb220dba..d2fa0f3a8 100644
--- a/server/process.c
+++ b/server/process.c
@@ -67,7 +67,7 @@ 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 void process_destroy( struct object *obj );
-static int process_get_esync_fd( 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 =
@@ -652,9 +652,10 @@ 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 )
+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;
}
diff --git a/server/protocol.def b/server/protocol.def
index 5f2d77bc7..cebe0e88e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4056,4 +4056,14 @@ struct handle_info
@REQ(get_esync_fd)
obj_handle_t handle; /* handle to the object */
@REPLY
+ int type; /* esync type (defined below) */
@END
+
+enum esync_type
+{
+ ESYNC_SEMAPHORE = 1,
+ ESYNC_AUTO_EVENT,
+ ESYNC_MANUAL_EVENT,
+ ESYNC_MANUAL_SERVER,
+ ESYNC_QUEUE,
+};
diff --git a/server/queue.c b/server/queue.c
index 0ea95291c..91de4a3a8 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -165,7 +165,7 @@ static void msg_queue_dump( struct object *obj, int verbose );
static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry );
static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry *entry );
static int msg_queue_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int msg_queue_get_esync_fd( struct object *obj );
+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 void msg_queue_destroy( struct object *obj );
static void msg_queue_poll_event( struct fd *fd, int event );
@@ -1040,9 +1040,10 @@ 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 )
+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;
}
diff --git a/server/thread.c b/server/thread.c
index e82d71f67..539cd0491 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -133,7 +133,7 @@ static const struct object_ops thread_apc_ops =
static void dump_thread( struct object *obj, int verbose );
static struct object_type *thread_get_type( struct object *obj );
static int thread_signaled( struct object *obj, struct wait_queue_entry *entry );
-static int thread_get_esync_fd( struct object *obj );
+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 void destroy_thread( struct object *obj );
@@ -396,9 +396,10 @@ static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
return mythread->state == TERMINATED && !mythread->exit_poll;
}
-static int thread_get_esync_fd( struct object *obj )
+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;
}
--
2.20.1

View File

@ -0,0 +1,107 @@
From 2887a75d89ec2552abb472603f8db70a6e70c1f8 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 15:39:37 -0500
Subject: [PATCH 25/83] server: Create eventfd descriptors for device manager
objects.
We don't have to worry about synchronization here because
wine_ntoskrnl_main_loop() is only ever called from one thread per winedevice
process.
This lets drivers like mountmgr finally work, and so winecfg can open the
Drives tab.
---
server/device.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/server/device.c b/server/device.c
index 67505d99c..aaec23495 100644
--- a/server/device.c
+++ b/server/device.c
@@ -39,6 +39,7 @@
#include "handle.h"
#include "request.h"
#include "process.h"
+#include "esync.h"
/* IRP object */
@@ -91,10 +92,12 @@ struct device_manager
struct list devices; /* list of devices */
struct list requests; /* list of pending irps across all devices */
struct wine_rb_tree kernel_objects; /* map of objects that have client side pointer associated */
+ int esync_fd; /* esync file descriptor */
};
static void device_manager_dump( struct object *obj, int verbose );
static int device_manager_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int device_manager_get_esync_fd( struct object *obj, enum esync_type *type );
static void device_manager_destroy( struct object *obj );
static const struct object_ops device_manager_ops =
@@ -105,7 +108,7 @@ static const struct object_ops device_manager_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
device_manager_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ device_manager_get_esync_fd, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -659,6 +662,9 @@ 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 );
}
@@ -695,6 +701,13 @@ 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 )
+{
+ struct device_manager *manager = (struct device_manager *)obj;
+ *type = ESYNC_MANUAL_SERVER;
+ return manager->esync_fd;
+}
+
static void device_manager_destroy( struct object *obj )
{
struct device_manager *manager = (struct device_manager *)obj;
@@ -725,6 +738,9 @@ static void device_manager_destroy( struct object *obj )
assert( !irp->file && !irp->async );
release_object( irp );
}
+
+ if (do_esync())
+ close( manager->esync_fd );
}
static struct device_manager *create_device_manager(void)
@@ -736,6 +752,9 @@ static struct device_manager *create_device_manager(void)
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;
}
@@ -874,6 +893,9 @@ DECL_HANDLER(get_next_device_request)
list_remove( &irp->mgr_entry );
list_init( &irp->mgr_entry );
if (!irp->file) release_object( irp ); /* no longer on manager queue */
+
+ if (do_esync() && list_empty( &manager->requests ))
+ esync_clear( manager->esync_fd );
}
}
else set_error( STATUS_PENDING );
--
2.20.1

View File

@ -0,0 +1,111 @@
From c6654c5479568c9bc5d5d64457c3cfbbf1c27f70 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 15:48:46 -0500
Subject: [PATCH 26/83] ntdll: Create esync objects for mutexes.
---
dlls/ntdll/esync.c | 39 +++++++++++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
server/protocol.def | 1 +
4 files changed, 45 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 4d708c6df..0798786df 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -97,6 +97,13 @@ struct event
struct esync obj;
};
+struct mutex
+{
+ struct esync obj;
+ DWORD tid;
+ int count; /* recursion count */
+};
+
/* 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. */
@@ -407,6 +414,38 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
+{
+ struct mutex *mutex;
+ NTSTATUS ret;
+ int fd;
+
+ TRACE("name %s, initial %d.\n",
+ attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
+
+ ret = create_esync( &fd, handle, access, attr, initial ? 0 : 1, 0 );
+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ {
+ mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
+ if (!mutex)
+ return STATUS_NO_MEMORY;
+
+ /* can't take ownership of the mutex if we didn't create it */
+ if (ret == STATUS_OBJECT_NAME_EXISTS)
+ initial = FALSE;
+
+ mutex->obj.type = ESYNC_MUTEX;
+ mutex->obj.fd = fd;
+ mutex->tid = initial ? GetCurrentThreadId() : 0;
+ mutex->count = initial ? 1 : 0;
+
+ add_to_list( *handle, &mutex->obj);
+ }
+
+ return ret;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 551257fbc..7af718027 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -29,6 +29,8 @@ extern NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
extern NTSTATUS esync_set_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 74b50cbc7..f6e507c2f 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -540,6 +540,9 @@ NTSTATUS WINAPI NtCreateMutant(OUT HANDLE* MutantHandle,
data_size_t len;
struct object_attributes *objattr;
+ if (do_esync())
+ return esync_create_mutex( MutantHandle, access, attr, InitialOwner );
+
if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
SERVER_START_REQ( create_mutex )
diff --git a/server/protocol.def b/server/protocol.def
index cebe0e88e..721ffb96d 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4064,6 +4064,7 @@ enum esync_type
ESYNC_SEMAPHORE = 1,
ESYNC_AUTO_EVENT,
ESYNC_MANUAL_EVENT,
+ ESYNC_MUTEX,
ESYNC_MANUAL_SERVER,
ESYNC_QUEUE,
};
--
2.20.1

View File

@ -0,0 +1,82 @@
From 26522cb866aa9d615a537195138fda6c24b43154 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 15:57:48 -0500
Subject: [PATCH 27/83] ntdll: Implement NtReleaseMutant().
---
dlls/ntdll/esync.c | 31 +++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 1 +
dlls/ntdll/sync.c | 3 +++
3 files changed, 35 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 0798786df..640625813 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -446,6 +446,37 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
return ret;
}
+NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
+{
+ struct mutex *mutex = esync_get_object( handle );
+ static const uint64_t value = 1;
+
+ TRACE("%p, %p.\n", handle, prev);
+
+ if (!mutex) return STATUS_INVALID_HANDLE;
+
+ /* 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( mutex->obj.fd, &value, sizeof(value) ) == -1)
+ return FILE_GetNtStatus();
+ }
+
+ return STATUS_SUCCESS;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 7af718027..47ab7815d 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -31,6 +31,7 @@ extern NTSTATUS esync_reset_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index f6e507c2f..3bc8fab8f 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -591,6 +591,9 @@ NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL
{
NTSTATUS status;
+ if (do_esync())
+ return esync_release_mutex( handle, prev_count );
+
SERVER_START_REQ( release_mutex )
{
req->handle = wine_server_obj_handle( handle );
--
2.20.1

View File

@ -0,0 +1,79 @@
From c811c73edf608b6c4120770a5dc34d99d39bde5d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 16:05:02 -0500
Subject: [PATCH 28/83] ntdll: Implement waiting on mutexes.
---
dlls/ntdll/esync.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 640625813..79d9c2e46 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -602,7 +602,25 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
{
for (i = 0; i < count; i++)
{
- fds[i].fd = objs[i] ? objs[i]->fd : -1;
+ struct esync *obj = objs[i];
+
+ if (obj && obj->type == ESYNC_MUTEX)
+ {
+ /* If we already own the mutex, return immediately. */
+ /* Note: This violates the assumption that the *first* object
+ * to be signaled will be returned. If that becomes a problem,
+ * we can always check the state of each object before waiting. */
+ struct mutex *mutex = (struct mutex *)obj;
+
+ if (mutex->tid == GetCurrentThreadId())
+ {
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ mutex->count++;
+ return i;
+ }
+ }
+
+ fds[i].fd = obj ? obj->fd : -1;
fds[i].events = POLLIN;
}
if (msgwait)
@@ -620,18 +638,20 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
/* 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 (objs[i])
+ if (obj)
{
int64_t value;
ssize_t size;
- if (objs[i]->type == ESYNC_MANUAL_EVENT || objs[i]->type == ESYNC_MANUAL_SERVER)
+ if (obj->type == ESYNC_MANUAL_EVENT || obj->type == ESYNC_MANUAL_SERVER)
{
/* Don't grab the object, just check if it's signaled. */
if (fds[i].revents & POLLIN)
@@ -646,6 +666,12 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
{
/* We found our object. */
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ if (obj->type == ESYNC_MUTEX)
+ {
+ struct mutex *mutex = (struct mutex *)obj;
+ mutex->tid = GetCurrentThreadId();
+ mutex->count = 1;
+ }
return i;
}
}
--
2.20.1

View File

@ -0,0 +1,211 @@
From 54a92fb487e2b3529974513a88de821edd4970f3 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 20:21:00 -0500
Subject: [PATCH 29/83] ntdll: Implement wait-all.
Most of the comments I would put here are covered in the code. I will just
amend:
Mutexes are extremely annoying. Microsoft should never have let you grab a
mutex more than once.
---
dlls/ntdll/esync.c | 162 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 149 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 79d9c2e46..1eab7367f 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -534,8 +534,10 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
LONGLONG timeleft;
LARGE_INTEGER now;
ULONGLONG end;
+ int64_t value;
+ ssize_t size;
+ int i, j;
int ret;
- int i;
NtQuerySystemTime( &now );
if (timeout)
@@ -648,9 +650,6 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
if (obj)
{
- int64_t value;
- ssize_t size;
-
if (obj->type == ESYNC_MANUAL_EVENT || obj->type == ESYNC_MANUAL_SERVER)
{
/* Don't grab the object, just check if it's signaled. */
@@ -688,21 +687,158 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
* we were waiting for. So keep waiting. */
NtQuerySystemTime( &now );
}
- else if (ret == 0)
+ 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;
+ for (i = 0; i < count; i++)
{
- TRACE("Wait timed out.\n");
- return STATUS_TIMEOUT;
+ 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 = (struct mutex *)obj;
+
+ if (mutex->tid == GetCurrentThreadId())
+ continue;
+ }
+
+ ret = do_poll( fds, 1, timeout ? &end : NULL );
+ if (ret <= 0)
+ goto err;
+
+ 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;
+ }
}
- else
+
+ /* Don't forget to wait for driver messages. */
+ if (msgwait)
{
- ERR("ppoll failed: %s\n", strerror(errno));
- return FILE_GetNtStatus();
+ fds[0].fd = ntdll_get_thread_data()->esync_queue_fd;
+ ret = do_poll( fds, 1, timeout ? &end : NULL );
+ if (ret <= 0)
+ goto err;
}
- }
+
+ /* 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;
+ }
+ if (msgwait)
+ {
+ fds[count].fd = ntdll_get_thread_data()->esync_queue_fd;
+ fds[count].events = POLLIN;
+ pollcount++;
+ }
+
+ /* Poll everything to see if they're still signaled. */
+ ret = poll( fds, pollcount, 0 );
+ if (ret == pollcount)
+ {
+ /* Quick, grab everything. */
+ for (i = 0; i < pollcount; i++)
+ {
+ struct esync *obj = objs[i];
+
+ switch (obj->type)
+ {
+ case ESYNC_MUTEX:
+ {
+ struct mutex *mutex = (struct mutex *)obj;
+ 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 FILE_GetNtStatus();
+ }
+
+ 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. */
+ for (i = 0; i < count; i++)
+ {
+ if (objs[i]->type == ESYNC_MUTEX)
+ {
+ struct mutex *mutex = (struct mutex *)objs[i];
+ mutex->tid = GetCurrentThreadId();
+ mutex->count++;
+ }
+ }
+ 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
{
- FIXME("Wait-all not implemented.\n");
- return STATUS_NOT_IMPLEMENTED;
+ ERR("ppoll failed: %s\n", strerror(errno));
+ return FILE_GetNtStatus();
}
}
--
2.20.1

View File

@ -0,0 +1,204 @@
From e5a2557a4de8eee42b216dfd89d2ef4ea980fb14 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 22:44:57 -0500
Subject: [PATCH 30/83] esync: Add a README.
---
README.esync | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 184 insertions(+)
create mode 100644 README.esync
diff --git a/README.esync b/README.esync
new file mode 100644
index 000000000..8fcb96901
--- /dev/null
+++ b/README.esync
@@ -0,0 +1,184 @@
+This is eventfd-based synchronization, or 'esync' for short. Turn it on with
+WINEESYNC=1 (note that it checks the presence and not the value); debug it
+with +esync.
+
+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.
+
+I had kind of figured that APCs just wouldn't work, but then poll() spat EINTR
+at me and I realized that this wasn't necessarily true. It seems that the
+server will suspend a thread when trying to deliver a system APC to a thread
+that's not waiting, and since the server has no idea that we're waiting it
+just suspends us. This of course interrupts poll(), which complains at us, and
+it turns out that just returning STATUS_USER_APC in that case is enough to
+make rpcrt4 happy.
+
+There are a couple things that this infrastructure can't handle, although
+surprisingly there aren't that many. In particular:
+* We can't return the previous count on a semaphore, since we have no way to
+ query the count on a semaphore through eventfd. Currently the code lies and
+ returns 1 every time. We can make this work (in a single process, or [if
+ necessary] in multiple processes through shared memory) by keeping a count
+ locally. We can't guarantee that it's the exact count at the moment the
+ semaphore was released, but I guess any program that runs into that race
+ shouldn't be depending on that fact anyway.
+* Similarly, we can't enforce the maximum count on a semaphore, since we have
+ no way to get the current count and subsequently compare it with the
+ maximum.
+* We can't use NtQueryMutant to get the mutant's owner or count if it lives in
+ a different process. If necessary we can use shared memory to make this
+ work, I guess, but see below.
+* User APCs don't work. However, it's not impossible to make them work; in
+ particular I think this could be relatively easily implemented by waiting on
+ another internal file descriptor when we execute an alertable wait.
+* 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.
+* We might hit the maximum number of open fd's. On my system the soft limit is
+ 1024 and the hard limit is 1048576. I'm inclined to hope this won't be an
+ issue, since a hypothetical Linux port of any application might just as well
+ use the same number of eventfds.
+* 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:
+* NtOpen* (aka Open*). This is just a matter of adding another open_esync
+ request analogous to those for other server primitives.
+* NtQuery*. This can be done to some degree (the difficulties are outlined
+ above). That said, these APIs aren't exposed through kernel32 in any way, so
+ I doubt anyone is going to be using them.
+* SignalObjectAndWait(). The server combines this into a single operation, but
+ according to MSDN it doesn't need to be atomic, so we can just signal the
+ appropriate object and wait, and woe betide anyone who gets in the way of
+ those two operations.
+* 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.
+
+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
\ No newline at end of file
--
2.20.1

View File

@ -0,0 +1,77 @@
From 41213edda4d5f04455c1b02561899bdebc5cf8e1 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 9 Jun 2018 23:34:27 -0500
Subject: [PATCH 31/83] ntdll: Implement NtSignalAndWaitForSingleObject().
---
dlls/ntdll/esync.c | 28 ++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 33 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 1eab7367f..130357ce6 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -842,3 +842,31 @@ err:
return FILE_GetNtStatus();
}
}
+
+NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable,
+ const LARGE_INTEGER *timeout )
+{
+ struct esync *obj = esync_get_object( signal );
+ NTSTATUS ret;
+
+ if (!obj) return STATUS_INVALID_HANDLE;
+
+ 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, NULL );
+ 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 );
+}
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 47ab7815d..ebf5c97c2 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -35,6 +35,8 @@ extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDE
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait,
+ BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
/* We have to synchronize on the fd cache CS so that our calls to receive_fd
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 3bc8fab8f..2f54bb4a1 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -1152,6 +1152,9 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWa
select_op_t select_op;
UINT flags = SELECT_INTERRUPTIBLE;
+ if (do_esync())
+ return esync_signal_and_wait( hSignalObject, hWaitObject, alertable, timeout );
+
if (!hSignalObject) return STATUS_INVALID_HANDLE;
if (alertable) flags |= SELECT_ALERTABLE;
--
2.20.1

View File

@ -0,0 +1,196 @@
From 4d40a736740ec865c3beee3cfa321477a1faca49 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 16:11:54 -0500
Subject: [PATCH 32/83] server, ntdll: Also store the esync type in the server.
And validate it there while we're at it.
We need to do this because CreateEvent() ignores the passed-in parameter
when opening an existing event.
---
dlls/ntdll/esync.c | 23 +++++++++++++++--------
server/esync.c | 28 ++++++++++++++++++++++++----
server/protocol.def | 2 ++
3 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 130357ce6..2b4555959 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -235,8 +235,10 @@ NTSTATUS esync_close( HANDLE handle )
return STATUS_INVALID_HANDLE;
}
-static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
- const OBJECT_ATTRIBUTES *attr, int initval, int flags)
+/* type is an in-out parameter; if the object already existed it returns the
+ * actual type. */
+static NTSTATUS create_esync(enum esync_type *type, int *fd, HANDLE *handle,
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags)
{
NTSTATUS ret;
data_size_t len;
@@ -254,11 +256,13 @@ static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
req->access = access;
req->initval = initval;
req->flags = flags;
+ req->type = *type;
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;
*fd = receive_fd( &fd_handle );
assert( wine_server_ptr_handle(fd_handle) == *handle );
}
@@ -275,6 +279,7 @@ static NTSTATUS create_esync(int *fd, HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
+ enum esync_type type = ESYNC_SEMAPHORE;
struct semaphore *semaphore;
NTSTATUS ret;
int fd = -1;
@@ -282,7 +287,7 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
TRACE("name %s, initial %d, max %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
- ret = create_esync( &fd, handle, access, attr, initial, EFD_SEMAPHORE );
+ ret = create_esync( &type, &fd, handle, access, attr, initial, EFD_SEMAPHORE );
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
@@ -321,24 +326,25 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
}
NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
- const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN initial )
+ const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial )
{
+ enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT);
struct event *event;
NTSTATUS ret;
int fd;
TRACE("name %s, %s-reset, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>",
- type == NotificationEvent ? "manual" : "auto", initial);
+ event_type == NotificationEvent ? "manual" : "auto", initial);
- ret = create_esync( &fd, handle, access, attr, initial, 0 );
+ ret = create_esync( &type, &fd, handle, access, attr, initial, 0 );
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
if (!event)
return STATUS_NO_MEMORY;
- event->obj.type = (type == NotificationEvent ? ESYNC_MANUAL_EVENT : ESYNC_AUTO_EVENT);
+ event->obj.type = type; /* note that the server might give us the real type */
event->obj.fd = fd;
add_to_list( *handle, &event->obj);
@@ -417,6 +423,7 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
{
+ enum esync_type type = ESYNC_MUTEX;
struct mutex *mutex;
NTSTATUS ret;
int fd;
@@ -424,7 +431,7 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
TRACE("name %s, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
- ret = create_esync( &fd, handle, access, attr, initial ? 0 : 1, 0 );
+ ret = create_esync( &type, &fd, handle, access, attr, initial ? 0 : 1, 0 );
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
diff --git a/server/esync.c b/server/esync.c
index 1f8c0d516..9ff1a847b 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -56,6 +56,7 @@ struct esync
{
struct object obj; /* object header */
int fd; /* eventfd file descriptor */
+ enum esync_type type;
};
static void esync_dump( struct object *obj, int verbose );
@@ -98,9 +99,16 @@ static void esync_destroy( struct object *obj )
close( esync->fd );
}
-struct esync *create_esync( struct object *root, const struct unicode_str *name,
- unsigned int attr, int initval, int flags,
- const struct security_descriptor *sd )
+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 struct esync *create_esync( struct object *root, const struct unicode_str *name,
+ unsigned int attr, int initval, int flags, enum esync_type type,
+ const struct security_descriptor *sd )
{
#ifdef HAVE_SYS_EVENTFD_H
struct esync *esync;
@@ -118,6 +126,17 @@ struct esync *create_esync( struct object *root, const struct unicode_str *name,
release_object( esync );
return NULL;
}
+ esync->type = type;
+ }
+ else
+ {
+ /* validate the type */
+ if (!type_matches( type, esync->type ))
+ {
+ release_object( &esync->obj );
+ set_error( STATUS_OBJECT_TYPE_MISMATCH );
+ return NULL;
+ }
}
}
return esync;
@@ -200,7 +219,7 @@ DECL_HANDLER(create_esync)
if (!objattr) return;
- if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->flags, sd )))
+ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->flags, req->type, sd )))
{
if (get_error() == STATUS_OBJECT_NAME_EXISTS)
reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );
@@ -208,6 +227,7 @@ DECL_HANDLER(create_esync)
reply->handle = alloc_handle_no_access_check( current->process, esync,
req->access, objattr->attributes );
+ reply->type = esync->type;
send_client_fd( current->process, esync->fd, reply->handle );
release_object( esync );
}
diff --git a/server/protocol.def b/server/protocol.def
index 721ffb96d..50735ee78 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4047,9 +4047,11 @@ struct handle_info
unsigned int access; /* wanted access rights */
int initval; /* initial value */
int flags; /* flags (EFD_SEMAPHORE or 0) */
+ int type; /* type of esync object (see below) */
VARARG(objattr,object_attributes); /* object attributes */
@REPLY
obj_handle_t handle; /* handle to the object */
+ int type; /* type of esync object (see below) */
@END
/* Retrieve the esync fd for an object. */
--
2.20.1

View File

@ -0,0 +1,185 @@
From 730ad81287cfb42dae5f4dd070cc0ea6979d5c54 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 00:29:27 -0500
Subject: [PATCH 33/83] ntdll, server: Implement NtOpenSemaphore().
---
dlls/ntdll/esync.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
server/esync.c | 30 +++++++++++++++++++++++
server/protocol.def | 12 +++++++++
5 files changed, 107 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 2b4555959..f014003fc 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -276,6 +276,37 @@ static NTSTATUS create_esync(enum esync_type *type, int *fd, HANDLE *handle,
return ret;
}
+static NTSTATUS open_esync( enum esync_type *type, int *fd, HANDLE *handle,
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
+{
+ NTSTATUS ret;
+ obj_handle_t fd_handle;
+ sigset_t sigset;
+
+ server_enter_uninterrupted_section( &fd_cache_section, &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;
+ *fd = receive_fd( &fd_handle );
+ assert( wine_server_ptr_handle(fd_handle) == *handle );
+ }
+ }
+ SERVER_END_REQ;
+ server_leave_uninterrupted_section( &fd_cache_section, &sigset );
+
+ TRACE("-> handle %p, fd %d.\n", *handle, *fd);
+ return ret;
+}
+
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
@@ -304,6 +335,35 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
return ret;
}
+NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr )
+{
+ enum esync_type type = ESYNC_SEMAPHORE;
+ struct semaphore *semaphore;
+ NTSTATUS ret;
+ int fd = -1;
+
+ TRACE("name %s.\n", debugstr_us(attr->ObjectName));
+
+ ret = open_esync( &type, &fd, handle, access, attr );
+ if (!ret)
+ {
+ semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
+ if (!semaphore)
+ return STATUS_NO_MEMORY;
+
+ semaphore->obj.type = ESYNC_SEMAPHORE;
+ semaphore->obj.fd = fd;
+
+ FIXME("Attempt to open a semaphore, this will not work.\n");
+ semaphore->max = 0xdeadbeef;
+
+ add_to_list( *handle, &semaphore->obj );
+ }
+
+ return ret;
+}
+
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
{
struct semaphore *semaphore = esync_get_object( handle );
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index ebf5c97c2..b0d756ec1 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -32,6 +32,8 @@ extern NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 2f54bb4a1..3b902e669 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -279,6 +279,9 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC
if ((ret = validate_open_object_attributes( attr ))) return ret;
+ if (do_esync())
+ return esync_open_semaphore( handle, access, attr );
+
SERVER_START_REQ( open_semaphore )
{
req->access = access;
diff --git a/server/esync.c b/server/esync.c
index 9ff1a847b..1f9602a43 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -235,6 +235,36 @@ DECL_HANDLER(create_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;
+
+ 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)
diff --git a/server/protocol.def b/server/protocol.def
index 50735ee78..7dbd0a6db 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4054,6 +4054,18 @@ struct handle_info
int type; /* type of esync object (see below) */
@END
+/* Open an esync object */
+@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 */
+@REPLY
+ obj_handle_t handle; /* handle to the event */
+ int type; /* type of esync object (above) */
+@END
+
/* Retrieve the esync fd for an object. */
@REQ(get_esync_fd)
obj_handle_t handle; /* handle to the object */
--
2.20.1

View File

@ -0,0 +1,78 @@
From 9eec3a23b2423759bf0e357a422ac0d410bd873f Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 00:42:15 -0500
Subject: [PATCH 34/83] ntdll: Implement NtOpenEvent().
---
dlls/ntdll/esync.c | 26 ++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 31 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index f014003fc..e6acc9eaf 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -413,6 +413,32 @@ NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
return ret;
}
+NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr )
+{
+ enum esync_type type = ESYNC_AUTO_EVENT;
+ struct event *event;
+ NTSTATUS ret;
+ int fd;
+
+ TRACE("name %s.\n", debugstr_us(attr->ObjectName));
+
+ ret = open_esync( &type, &fd, handle, access, attr );
+ if (!ret)
+ {
+ event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
+ if (!event)
+ return STATUS_NO_MEMORY;
+
+ event->obj.type = type;
+ event->obj.fd = fd;
+
+ add_to_list( *handle, &event->obj );
+ }
+
+ return ret;
+}
+
NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
{
struct event *event = esync_get_object( handle );
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index b0d756ec1..1af343ef4 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -34,6 +34,8 @@ extern NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
extern NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 3b902e669..b39c17c64 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -398,6 +398,9 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT
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;
--
2.20.1

View File

@ -0,0 +1,82 @@
From 143d6f220efa6b54571a5ef1b18f5faced8543af Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 00:44:54 -0500
Subject: [PATCH 35/83] ntdll: Implement NtOpenMutant().
---
dlls/ntdll/esync.c | 30 ++++++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 35 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index e6acc9eaf..9da300853 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -539,6 +539,36 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
return ret;
}
+NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr )
+{
+ enum esync_type type = ESYNC_MUTEX;
+ struct mutex *mutex;
+ NTSTATUS ret;
+ int fd;
+
+ TRACE("name %s.\n", debugstr_us(attr->ObjectName));
+
+ ret = open_esync( &type, &fd, handle, access, attr );
+ if (!ret)
+ {
+ mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
+ if (!mutex)
+ return STATUS_NO_MEMORY;
+
+ mutex->obj.type = ESYNC_MUTEX;
+ mutex->obj.fd = fd;
+
+ FIXME("Attempt to open a mutex; this will not work.\n");
+ mutex->tid = 0;
+ mutex->count = 0;
+
+ add_to_list( *handle, &mutex->obj );
+ }
+
+ return ret;
+}
+
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
{
struct mutex *mutex = esync_get_object( handle );
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 1af343ef4..f79b9a06c 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -36,6 +36,8 @@ extern NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
+ const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index b39c17c64..2f8d6fe5c 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -575,6 +575,9 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A
if ((status = validate_open_object_attributes( attr ))) return status;
+ if (do_esync())
+ return esync_open_mutex( handle, access, attr );
+
SERVER_START_REQ( open_mutex )
{
req->access = access;
--
2.20.1

View File

@ -0,0 +1,115 @@
From 52efa515c406f66dc16a82145ec116e90c64ecee Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 12:08:51 -0500
Subject: [PATCH 36/83] ntdll: Record the current count of a semaphore locally.
And check it against the maximum, and return it if requested.
Of course, this won't work across processes.
This should be thread-safe.
---
dlls/ntdll/esync.c | 39 ++++++++++++++++++++++++++++++++++-----
1 file changed, 34 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 9da300853..25e049291 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -90,6 +90,7 @@ struct semaphore
{
struct esync obj;
int max;
+ int count;
};
struct event
@@ -328,6 +329,7 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
semaphore->obj.type = ESYNC_SEMAPHORE;
semaphore->obj.fd = fd;
semaphore->max = max;
+ semaphore->count = initial;
add_to_list( *handle, &semaphore->obj );
}
@@ -356,6 +358,7 @@ NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
semaphore->obj.fd = fd;
FIXME("Attempt to open a semaphore, this will not work.\n");
+ semaphore->count = 0;
semaphore->max = 0xdeadbeef;
add_to_list( *handle, &semaphore->obj );
@@ -368,16 +371,27 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
{
struct semaphore *semaphore = esync_get_object( handle );
uint64_t count64 = count;
+ ULONG current;
TRACE("%p, %d, %p.\n", handle, count, prev);
if (!semaphore) return STATUS_INVALID_HANDLE;
- if (prev)
+ /* FIXME: This won't work across processes. In that case it may be best to
+ * use shared memory. */
+ do
{
- FIXME("Can't write previous value.\n");
- *prev = 1;
- }
+ current = semaphore->count;
+
+ if (count + current > semaphore->max)
+ return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
+ } while (interlocked_cmpxchg( &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( semaphore->obj.fd, &count64, sizeof(count64) ) == -1)
return FILE_GetNtStatus();
@@ -794,6 +808,15 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
mutex->tid = GetCurrentThreadId();
mutex->count = 1;
}
+ else if (obj->type == ESYNC_SEMAPHORE)
+ {
+ struct semaphore *semaphore = (struct semaphore *)obj;
+ /* 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. */
+ interlocked_xchg_add( &semaphore->count, -1 );
+ }
return i;
}
}
@@ -932,7 +955,8 @@ tryagain:
}
/* If we got here, we successfully waited on every object. */
- /* Make sure to let ourselves know that we grabbed the mutexes. */
+ /* Make sure to let ourselves know that we grabbed the mutexes
+ * and semaphores. */
for (i = 0; i < count; i++)
{
if (objs[i]->type == ESYNC_MUTEX)
@@ -941,6 +965,11 @@ tryagain:
mutex->tid = GetCurrentThreadId();
mutex->count++;
}
+ else if (objs[i]->type == ESYNC_SEMAPHORE)
+ {
+ struct semaphore *semaphore = (struct semaphore *)objs[i];
+ interlocked_xchg_add( &semaphore->count, -1 );
+ }
}
TRACE("Wait successful.\n");
return STATUS_SUCCESS;
--
2.20.1

View File

@ -0,0 +1,50 @@
From 9bd148fedb51a9fc6672b64c7a9f2346c45cb458 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 19:08:18 -0500
Subject: [PATCH 37/83] server: Implement esync_map_access().
---
server/esync.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/server/esync.c b/server/esync.c
index 1f9602a43..62e6df30b 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -60,6 +60,7 @@ struct esync
};
static void esync_dump( struct object *obj, int verbose );
+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 =
@@ -74,7 +75,7 @@ const struct object_ops esync_ops =
NULL, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
- no_map_access, /* map_access */
+ esync_map_access, /* map_access */
default_get_sd, /* get_sd */
default_set_sd, /* set_sd */
no_lookup_name, /* lookup_name */
@@ -93,6 +94,16 @@ static void esync_dump( struct object *obj, int verbose )
fprintf( stderr, "esync fd=%d\n", 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;
--
2.20.1

View File

@ -0,0 +1,38 @@
From 0d9f118eb9601f98ff5f631effa3197477ab48b0 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Mon, 11 Jun 2018 16:43:24 -0500
Subject: [PATCH 38/83] server: Alter conditions in is_queue_hung().
Unusually, I've elected not to guard this with do_esync(). I think this is a better way of checking for a hung queue in general, both because it's simpler and because it makes more sense: a queue is hung if it has messages it hasn't processed, not necessarily if it isn't actively waiting for messages. For example, a queue could be not associated with a window, and only waiting for messages when it makes sense to do so (due to some internal program logic). Or a thread could elect to poll its message queue with, say, GetQueueStatus(), and only get messages when there are messages to process.
And, of course, this stops esync threads from being considered hung, since obviously the server has no idea that they are indeed waiting on their own queue. Normally nobody cares about this, but as of f6b0ce3c the server refuses to deliver hooks to a hung queue, and so we need to avoid a queue being wrongly considered hung.
---
server/queue.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/server/queue.c b/server/queue.c
index 91de4a3a8..e217a5e6a 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -975,17 +975,7 @@ static void cleanup_results( struct msg_queue *queue )
/* check if the thread owning the queue is hung (not checking for messages) */
static int is_queue_hung( struct msg_queue *queue )
{
- struct wait_queue_entry *entry;
-
- if (current_time - queue->last_get_msg <= 5 * TICKS_PER_SEC)
- return 0; /* less than 5 seconds since last get message -> not hung */
-
- LIST_FOR_EACH_ENTRY( entry, &queue->obj.wait_queue, struct wait_queue_entry, entry )
- {
- if (get_wait_queue_thread(entry)->queue == queue)
- return 0; /* thread is waiting on queue -> not hung */
- }
- return 1;
+ return is_signaled( queue ) && (current_time - queue->last_get_msg > 5 * TICKS_PER_SEC);
}
static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry )
--
2.20.1

View File

@ -0,0 +1,106 @@
From fafd82460f1a8ee69feb9f2921889abd884d7926 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 14:33:38 -0500
Subject: [PATCH 39/83] ntdll, server: Allow DuplicateHandle() to succeed by
implementing esync_get_esync_fd().
Note that this only works for events.
---
dlls/ntdll/esync.c | 45 ++++++++++++++++++++++++++++++++++++++++++---
server/esync.c | 10 +++++++++-
2 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 25e049291..4adf01a11 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -206,9 +206,48 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
TRACE("Got fd %d for handle %p.\n", fd, handle);
- esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
- esync->fd = fd;
- esync->type = type;
+ switch (type)
+ {
+ case ESYNC_SEMAPHORE:
+ {
+ struct semaphore *semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
+ semaphore->obj.type = ESYNC_SEMAPHORE;
+ semaphore->obj.fd = fd;
+
+ FIXME("Attempt to duplicate a semaphore; this will not work.\n");
+ semaphore->max = 0xdeadbeef;
+ semaphore->count = 0;
+ esync = &semaphore->obj;
+ break;
+ }
+ case ESYNC_AUTO_EVENT:
+ case ESYNC_MANUAL_EVENT:
+ {
+ struct event *event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
+ event->obj.type = type;
+ event->obj.fd = fd;
+ esync = &event->obj;
+ break;
+ }
+ case ESYNC_MUTEX:
+ {
+ struct mutex *mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
+ mutex->obj.type = type;
+ mutex->obj.fd = fd;
+
+ FIXME("Attempt to duplicate a mutex; this will not work.\n");
+ mutex->tid = 0;
+ mutex->count = 0;
+ esync = &mutex->obj;
+ break;
+ }
+ case ESYNC_MANUAL_SERVER:
+ case ESYNC_QUEUE:
+ esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
+ esync->fd = fd;
+ esync->type = type;
+ break;
+ }
add_to_list( handle, esync );
diff --git a/server/esync.c b/server/esync.c
index 62e6df30b..5dd38c42a 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -60,6 +60,7 @@ struct esync
};
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 );
@@ -71,7 +72,7 @@ const struct object_ops esync_ops =
no_add_queue, /* add_queue */
NULL, /* remove_queue */
NULL, /* signaled */
- NULL, /* get_esync_fd */
+ esync_get_esync_fd, /* get_esync_fd */
NULL, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -94,6 +95,13 @@ static void esync_dump( struct object *obj, int verbose )
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. */
--
2.20.1

View File

@ -0,0 +1,107 @@
From 034af084be3e4867cdf1243148ce5c6be80703ec Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 15 Jun 2018 11:01:44 -0500
Subject: [PATCH 40/83] server: Create eventfd descriptors for timers.
---
dlls/ntdll/esync.c | 1 +
server/protocol.def | 1 +
server/timer.c | 18 +++++++++++++++++-
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 4adf01a11..9d9d767c4 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -241,6 +241,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
esync = &mutex->obj;
break;
}
+ case ESYNC_AUTO_SERVER:
case ESYNC_MANUAL_SERVER:
case ESYNC_QUEUE:
esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
diff --git a/server/protocol.def b/server/protocol.def
index 7dbd0a6db..d14fdb607 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4079,6 +4079,7 @@ enum esync_type
ESYNC_AUTO_EVENT,
ESYNC_MANUAL_EVENT,
ESYNC_MUTEX,
+ ESYNC_AUTO_SERVER,
ESYNC_MANUAL_SERVER,
ESYNC_QUEUE,
};
diff --git a/server/timer.c b/server/timer.c
index 109aa0ff9..e0760ba11 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -36,6 +36,7 @@
#include "file.h"
#include "handle.h"
#include "request.h"
+#include "esync.h"
struct timer
{
@@ -48,11 +49,13 @@ struct timer
struct thread *thread; /* thread that set the APC function */
client_ptr_t callback; /* callback APC function */
client_ptr_t arg; /* callback argument */
+ int esync_fd; /* esync file descriptor */
};
static void timer_dump( struct object *obj, int verbose );
static struct object_type *timer_get_type( struct object *obj );
static int timer_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int timer_get_esync_fd( struct object *obj, enum esync_type *type );
static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry );
static unsigned int timer_map_access( struct object *obj, unsigned int access );
static void timer_destroy( struct object *obj );
@@ -65,7 +68,7 @@ static const struct object_ops timer_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
timer_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ timer_get_esync_fd, /* get_esync_fd */
timer_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -99,6 +102,9 @@ static struct timer *create_timer( struct object *root, const struct unicode_str
timer->period = 0;
timer->timeout = NULL;
timer->thread = NULL;
+
+ if (do_esync())
+ timer->esync_fd = esync_create_fd( 0, 0 );
}
}
return timer;
@@ -171,6 +177,9 @@ 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 );
}
timer->when = (expire <= 0) ? current_time - expire : max( expire, current_time );
timer->period = period;
@@ -202,6 +211,13 @@ 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 )
+{
+ struct timer *timer = (struct timer *)obj;
+ *type = timer->manual ? ESYNC_MANUAL_SERVER : ESYNC_AUTO_SERVER;
+ return timer->esync_fd;
+}
+
static void timer_satisfied( struct object *obj, struct wait_queue_entry *entry )
{
struct timer *timer = (struct timer *)obj;
--
2.20.1

View File

@ -0,0 +1,201 @@
From 2ca666409e42b5ecf78b32a583d3ba95cbcce356 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 23:12:16 -0500
Subject: [PATCH 41/83] server: Allocate shared memory segments for semaphores
and mutexes.
As has been described in the README, these two objects have state that can't
be expressed (or read from) the eventfd descriptor. Namely, for semaphores
we need to know the current lock count, and for mutexes we need to know the
owner tid and the recursion count. To make these work across processes, we
store them in a global shared memory section.
This patch has no effect by itself; it's for separation.
We use the WINEPREFIX dir to discriminate shm sections for simultaneously
running servers; this is based off of code in libwine (specifically
init_server_dir()).
---
server/esync.c | 65 +++++++++++++++++++++++++++++++++++++++++++++
server/esync.h | 1 +
server/main.c | 4 +++
server/protocol.def | 2 ++
4 files changed, 72 insertions(+)
diff --git a/server/esync.c b/server/esync.c
index 5dd38c42a..e9a1ec15e 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -21,17 +21,26 @@
#include "config.h"
#include "wine/port.h"
+#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef HAVE_SYS_EVENTFD_H
# include <sys/eventfd.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#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 "wine/library.h"
#include "handle.h"
#include "request.h"
@@ -52,11 +61,46 @@ int do_esync(void)
#endif
}
+static char shm_name[29];
+static int shm_fd;
+static off_t shm_size;
+
+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 (stat( wine_get_config_dir(), &st ) == -1)
+ fatal_error( "cannot stat %s\n", wine_get_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 );
+
+ shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 );
+ if (shm_fd == -1)
+ perror( "shm_open" );
+
+ shm_size = sysconf( _SC_PAGESIZE );
+ if (ftruncate( shm_fd, shm_size ) == -1)
+ perror( "ftruncate" );
+
+ atexit( shm_cleanup );
+}
+
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 */
};
static void esync_dump( struct object *obj, int verbose );
@@ -146,6 +190,25 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
return NULL;
}
esync->type = type;
+ if (type == ESYNC_SEMAPHORE || type == ESYNC_MUTEX)
+ {
+ /* 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 += sysconf( _SC_PAGESIZE );
+ if (ftruncate( shm_fd, shm_size ) == -1)
+ {
+ fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
+ shm_name, shm_size );
+ perror( "ftruncate" );
+ }
+ }
+ }
+ else
+ esync->shm_idx = 0;
}
else
{
@@ -247,6 +310,7 @@ DECL_HANDLER(create_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 );
}
@@ -278,6 +342,7 @@ DECL_HANDLER(open_esync)
}
reply->type = esync->type;
+ reply->shm_idx = esync->shm_idx;
send_client_fd( current->process, esync->fd, reply->handle );
release_object( esync );
diff --git a/server/esync.h b/server/esync.h
index 2687c72e4..aeb58c546 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -19,6 +19,7 @@
*/
extern int do_esync(void);
+void esync_init(void);
int esync_create_fd( int initval, int flags );
void esync_wake_up( struct object *obj );
void esync_clear( int fd );
diff --git a/server/main.c b/server/main.c
index 13af3b9fe..2a91f5ec8 100644
--- a/server/main.c
+++ b/server/main.c
@@ -36,6 +36,7 @@
#include "file.h"
#include "thread.h"
#include "request.h"
+#include "esync.h"
#include "wine/library.h"
/* command-line options */
@@ -142,6 +143,9 @@ 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() );
init_scheduler();
init_signals();
diff --git a/server/protocol.def b/server/protocol.def
index d14fdb607..c2b554490 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4052,6 +4052,7 @@ struct handle_info
@REPLY
obj_handle_t handle; /* handle to the object */
int type; /* type of esync object (see below) */
+ unsigned int shm_idx; /* this object's index into the shm section */
@END
/* Open an esync object */
@@ -4064,6 +4065,7 @@ struct handle_info
@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 */
@END
/* Retrieve the esync fd for an object. */
--
2.20.1

View File

@ -0,0 +1,696 @@
From 48e622c107b931be17b6b4c424e71e1dea6ef86f Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Mon, 11 Jun 2018 13:10:36 -0500
Subject: [PATCH 42/83] ntdll: Use shared memory segments to store semaphore
and mutex state.
This patch is pretty big, because it restructures the underlying esync
objects. Instead of wrapping a struct esync, the semaphore and mutex structs
are now contained within it, as a pointer to shared memory. This also means
that we can move a lot of common code to create_esync() and open_esync()
since now we don't have to allocate separate structures.
---
dlls/ntdll/esync.c | 349 +++++++++++++++++++-------------------------
dlls/ntdll/esync.h | 1 +
dlls/ntdll/thread.c | 4 +
3 files changed, 155 insertions(+), 199 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 9d9d767c4..ce62446e6 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -32,6 +32,7 @@
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
+#include <stdio.h>
#ifdef HAVE_SYS_EVENTFD_H
# include <sys/eventfd.h>
#endif
@@ -84,27 +85,75 @@ struct esync
{
enum esync_type type; /* defined in protocol.def */
int fd;
+ void *shm; /* pointer to shm section */
};
struct semaphore
{
- struct esync obj;
int max;
int count;
};
-struct event
-{
- struct esync obj;
-};
-
struct mutex
{
- struct esync obj;
DWORD tid;
int count; /* recursion count */
};
+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;
+
+void esync_init(void)
+{
+ struct stat st;
+
+ if (stat( wine_get_config_dir(), &st ) == -1)
+ ERR("Cannot stat %s\n", wine_get_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)
+ ERR("Failed to initialize shared memory: %s\n", strerror( errno ));
+
+ pagesize = sysconf( _SC_PAGESIZE );
+
+ shm_addrs = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, 128 * sizeof(shm_addrs[0]) );
+ shm_addrs_size = 128;
+}
+
+static void *get_shm( unsigned int idx )
+{
+ int entry = (idx * 8) / pagesize;
+ int offset = (idx * 8) % pagesize;
+
+ if (entry >= shm_addrs_size)
+ {
+ shm_addrs_size *= 2;
+ if (!(shm_addrs = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, shm_addrs, shm_addrs_size )))
+ ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_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 (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
+ munmap( addr, pagesize ); /* someone beat us to it */
+ }
+
+ return (void *)((unsigned long)shm_addrs[entry] + offset);
+}
+
/* 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. */
@@ -206,49 +255,13 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
TRACE("Got fd %d for handle %p.\n", fd, handle);
- switch (type)
- {
- case ESYNC_SEMAPHORE:
- {
- struct semaphore *semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
- semaphore->obj.type = ESYNC_SEMAPHORE;
- semaphore->obj.fd = fd;
-
- FIXME("Attempt to duplicate a semaphore; this will not work.\n");
- semaphore->max = 0xdeadbeef;
- semaphore->count = 0;
- esync = &semaphore->obj;
- break;
- }
- case ESYNC_AUTO_EVENT:
- case ESYNC_MANUAL_EVENT:
- {
- struct event *event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
- event->obj.type = type;
- event->obj.fd = fd;
- esync = &event->obj;
- break;
- }
- case ESYNC_MUTEX:
- {
- struct mutex *mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
- mutex->obj.type = type;
- mutex->obj.fd = fd;
+ esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
+ esync->fd = fd;
+ esync->type = type;
- FIXME("Attempt to duplicate a mutex; this will not work.\n");
- mutex->tid = 0;
- mutex->count = 0;
- esync = &mutex->obj;
- break;
- }
- case ESYNC_AUTO_SERVER:
- case ESYNC_MANUAL_SERVER:
- case ESYNC_QUEUE:
- esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
- esync->fd = fd;
- esync->type = type;
- break;
- }
+ if (type == ESYNC_SEMAPHORE || type == ESYNC_MUTEX)
+ FIXME("Attempt to duplicate a semaphore or mutex; this will not work.\n");
+ esync->shm = NULL;
add_to_list( handle, esync );
@@ -276,16 +289,16 @@ NTSTATUS esync_close( HANDLE handle )
return STATUS_INVALID_HANDLE;
}
-/* type is an in-out parameter; if the object already existed it returns the
- * actual type. */
-static NTSTATUS create_esync(enum esync_type *type, int *fd, HANDLE *handle,
- ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags)
+static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags )
{
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;
@@ -297,32 +310,47 @@ static NTSTATUS create_esync(enum esync_type *type, int *fd, HANDLE *handle,
req->access = access;
req->initval = initval;
req->flags = flags;
- req->type = *type;
+ req->type = type;
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;
- *fd = receive_fd( &fd_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_section, &sigset );
- TRACE("-> handle %p, fd %d.\n", *handle, *fd);
+ if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ {
+ struct esync *obj = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*obj) );
+ if (!obj) return STATUS_NO_MEMORY;
+
+ obj->type = type;
+ obj->fd = fd;
+ obj->shm = shm_idx ? get_shm( shm_idx ) : 0;
+
+ add_to_list( *handle, obj );
+
+ TRACE("-> handle %p, fd %d, shm index %d.\n", *handle, fd, shm_idx);
+ }
RtlFreeHeap( GetProcessHeap(), 0, objattr );
return ret;
}
-static NTSTATUS open_esync( enum esync_type *type, int *fd, HANDLE *handle,
+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_section, &sigset );
SERVER_START_REQ( open_esync )
@@ -330,48 +358,55 @@ static NTSTATUS open_esync( enum esync_type *type, int *fd, HANDLE *handle,
req->access = access;
req->attributes = attr->Attributes;
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
- req->type = *type;
+ 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;
- *fd = receive_fd( &fd_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_section, &sigset );
- TRACE("-> handle %p, fd %d.\n", *handle, *fd);
+ if (!ret)
+ {
+ struct esync *obj = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*obj) );
+ if (!obj) return STATUS_NO_MEMORY;
+
+ obj->type = type;
+ obj->fd = fd;
+ obj->shm = shm_idx ? get_shm( shm_idx ) : 0;
+
+ add_to_list( *handle, obj );
+
+ TRACE("-> handle %p, fd %d.\n", *handle, fd);
+ }
return ret;
}
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
- enum esync_type type = ESYNC_SEMAPHORE;
- struct semaphore *semaphore;
NTSTATUS ret;
- int fd = -1;
TRACE("name %s, initial %d, max %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
- ret = create_esync( &type, &fd, handle, access, attr, initial, EFD_SEMAPHORE );
- if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, EFD_SEMAPHORE );
+ if (!ret)
{
- semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
- if (!semaphore)
- return STATUS_NO_MEMORY;
-
- semaphore->obj.type = ESYNC_SEMAPHORE;
- semaphore->obj.fd = fd;
+ /* Initialize the shared memory portion.
+ * Note we store max here (even though we don't need to) just to keep
+ * it the same size as the mutex's shm portion. */
+ struct esync *obj = esync_get_object( *handle );
+ struct semaphore *semaphore = obj->shm;
semaphore->max = max;
semaphore->count = initial;
-
- add_to_list( *handle, &semaphore->obj );
}
return ret;
@@ -380,45 +415,23 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
- enum esync_type type = ESYNC_SEMAPHORE;
- struct semaphore *semaphore;
- NTSTATUS ret;
- int fd = -1;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- ret = open_esync( &type, &fd, handle, access, attr );
- if (!ret)
- {
- semaphore = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*semaphore) );
- if (!semaphore)
- return STATUS_NO_MEMORY;
-
- semaphore->obj.type = ESYNC_SEMAPHORE;
- semaphore->obj.fd = fd;
-
- FIXME("Attempt to open a semaphore, this will not work.\n");
- semaphore->count = 0;
- semaphore->max = 0xdeadbeef;
-
- add_to_list( *handle, &semaphore->obj );
- }
-
- return ret;
+ return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
}
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
{
- struct semaphore *semaphore = esync_get_object( handle );
+ struct esync *obj = esync_get_object( handle );
+ struct semaphore *semaphore;
uint64_t count64 = count;
ULONG current;
TRACE("%p, %d, %p.\n", handle, count, prev);
- if (!semaphore) return STATUS_INVALID_HANDLE;
+ if (!obj) return STATUS_INVALID_HANDLE;
+ semaphore = obj->shm;
- /* FIXME: This won't work across processes. In that case it may be best to
- * use shared memory. */
do
{
current = semaphore->count;
@@ -433,7 +446,7 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
* write(). The fact that we were able to increase the count means that we
* have permission to actually write that many releases to the semaphore. */
- if (write( semaphore->obj.fd, &count64, sizeof(count64) ) == -1)
+ if (write( obj->fd, &count64, sizeof(count64) ) == -1)
return FILE_GetNtStatus();
return STATUS_SUCCESS;
@@ -443,64 +456,30 @@ 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);
- struct event *event;
- NTSTATUS ret;
- int fd;
TRACE("name %s, %s-reset, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>",
event_type == NotificationEvent ? "manual" : "auto", initial);
- ret = create_esync( &type, &fd, handle, access, attr, initial, 0 );
- if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
- {
- event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
- if (!event)
- return STATUS_NO_MEMORY;
-
- event->obj.type = type; /* note that the server might give us the real type */
- event->obj.fd = fd;
-
- add_to_list( *handle, &event->obj);
- }
-
- return ret;
+ return create_esync( type, handle, access, attr, initial, 0 );
}
NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
- enum esync_type type = ESYNC_AUTO_EVENT;
- struct event *event;
- NTSTATUS ret;
- int fd;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- ret = open_esync( &type, &fd, handle, access, attr );
- if (!ret)
- {
- event = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*event) );
- if (!event)
- return STATUS_NO_MEMORY;
-
- event->obj.type = type;
- event->obj.fd = fd;
-
- add_to_list( *handle, &event->obj );
- }
-
- return ret;
+ return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
}
NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
{
- struct event *event = esync_get_object( handle );
+ struct esync *obj = esync_get_object( handle );
static const uint64_t value = 1;
TRACE("%p.\n", handle);
- if (!event) return STATUS_INVALID_HANDLE;
+ if (!obj) return STATUS_INVALID_HANDLE;
if (prev)
{
@@ -508,7 +487,7 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
*prev = 1;
}
- if (write( event->obj.fd, &value, sizeof(value) ) == -1)
+ if (write( obj->fd, &value, sizeof(value) ) == -1)
return FILE_GetNtStatus();
return STATUS_SUCCESS;
@@ -516,12 +495,12 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
{
- struct event *event = esync_get_object( handle );
+ struct esync *obj = esync_get_object( handle );
static uint64_t value;
TRACE("%p.\n", handle);
- if (!event) return STATUS_INVALID_HANDLE;
+ if (!obj) return STATUS_INVALID_HANDLE;
if (prev)
{
@@ -530,19 +509,19 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
}
/* we don't care about the return value */
- read( event->obj.fd, &value, sizeof(value) );
+ read( obj->fd, &value, sizeof(value) );
return STATUS_SUCCESS;
}
NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
{
- struct event *event = esync_get_object( handle );
+ struct esync *obj = esync_get_object( handle );
static uint64_t value = 1;
TRACE("%p.\n", handle);
- if (!event) return STATUS_INVALID_HANDLE;
+ if (!obj) return STATUS_INVALID_HANDLE;
if (prev)
{
@@ -553,9 +532,9 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
/* This isn't really correct; an application could miss the write.
* Unfortunately we can't really do much better. Fortunately this is rarely
* used (and publicly deprecated). */
- if (write( event->obj.fd, &value, sizeof(value) ) == -1)
+ if (write( obj->fd, &value, sizeof(value) ) == -1)
return FILE_GetNtStatus();
- read( event->obj.fd, &value, sizeof(value) );
+ read( obj->fd, &value, sizeof(value) );
return STATUS_SUCCESS;
}
@@ -563,31 +542,19 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
{
- enum esync_type type = ESYNC_MUTEX;
- struct mutex *mutex;
NTSTATUS ret;
- int fd;
TRACE("name %s, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
- ret = create_esync( &type, &fd, handle, access, attr, initial ? 0 : 1, 0 );
- if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
+ ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
+ if (!ret)
{
- mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
- if (!mutex)
- return STATUS_NO_MEMORY;
-
- /* can't take ownership of the mutex if we didn't create it */
- if (ret == STATUS_OBJECT_NAME_EXISTS)
- initial = FALSE;
-
- mutex->obj.type = ESYNC_MUTEX;
- mutex->obj.fd = fd;
+ /* Initialize the shared memory portion. */
+ struct esync *obj = esync_get_object( *handle );
+ struct mutex *mutex = obj->shm;
mutex->tid = initial ? GetCurrentThreadId() : 0;
mutex->count = initial ? 1 : 0;
-
- add_to_list( *handle, &mutex->obj);
}
return ret;
@@ -596,41 +563,21 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
- enum esync_type type = ESYNC_MUTEX;
- struct mutex *mutex;
- NTSTATUS ret;
- int fd;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- ret = open_esync( &type, &fd, handle, access, attr );
- if (!ret)
- {
- mutex = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*mutex) );
- if (!mutex)
- return STATUS_NO_MEMORY;
-
- mutex->obj.type = ESYNC_MUTEX;
- mutex->obj.fd = fd;
-
- FIXME("Attempt to open a mutex; this will not work.\n");
- mutex->tid = 0;
- mutex->count = 0;
-
- add_to_list( *handle, &mutex->obj );
- }
-
- return ret;
+ return open_esync( ESYNC_MUTEX, handle, access, attr );
}
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
{
- struct mutex *mutex = esync_get_object( handle );
+ struct esync *obj = esync_get_object( handle );
+ struct mutex *mutex;
static const uint64_t value = 1;
TRACE("%p, %p.\n", handle, prev);
- if (!mutex) return STATUS_INVALID_HANDLE;
+ if (!obj) return STATUS_INVALID_HANDLE;
+ mutex = obj->shm;
/* This is thread-safe, because the only thread that can change the tid to
* or from our tid is ours. */
@@ -647,7 +594,7 @@ NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
* theirs. */
mutex->tid = 0;
- if (write( mutex->obj.fd, &value, sizeof(value) ) == -1)
+ if (write( obj->fd, &value, sizeof(value) ) == -1)
return FILE_GetNtStatus();
}
@@ -789,7 +736,7 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
/* Note: This violates the assumption that the *first* object
* to be signaled will be returned. If that becomes a problem,
* we can always check the state of each object before waiting. */
- struct mutex *mutex = (struct mutex *)obj;
+ struct mutex *mutex = obj->shm;
if (mutex->tid == GetCurrentThreadId())
{
@@ -844,13 +791,17 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
if (obj->type == ESYNC_MUTEX)
{
- struct mutex *mutex = (struct mutex *)obj;
+ 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). */
mutex->tid = GetCurrentThreadId();
mutex->count = 1;
}
else if (obj->type == ESYNC_SEMAPHORE)
{
- struct semaphore *semaphore = (struct semaphore *)obj;
+ 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
@@ -914,7 +865,7 @@ tryagain:
if (obj && obj->type == ESYNC_MUTEX)
{
/* It might be ours. */
- struct mutex *mutex = (struct mutex *)obj;
+ struct mutex *mutex = obj->shm;
if (mutex->tid == GetCurrentThreadId())
continue;
@@ -967,7 +918,7 @@ tryagain:
{
case ESYNC_MUTEX:
{
- struct mutex *mutex = (struct mutex *)obj;
+ struct mutex *mutex = obj->shm;
if (mutex->tid == GetCurrentThreadId())
break;
/* otherwise fall through */
@@ -1001,13 +952,13 @@ tryagain:
{
if (objs[i]->type == ESYNC_MUTEX)
{
- struct mutex *mutex = (struct mutex *)objs[i];
+ struct mutex *mutex = objs[i]->shm;
mutex->tid = GetCurrentThreadId();
mutex->count++;
}
else if (objs[i]->type == ESYNC_SEMAPHORE)
{
- struct semaphore *semaphore = (struct semaphore *)objs[i];
+ struct semaphore *semaphore = objs[i]->shm;
interlocked_xchg_add( &semaphore->count, -1 );
}
}
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index f79b9a06c..88f491e5a 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -19,6 +19,7 @@
*/
extern int do_esync(void) DECLSPEC_HIDDEN;
+extern void esync_init(void) DECLSPEC_HIDDEN;
extern NTSTATUS esync_close( HANDLE handle ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 87134db09..04aa8dff8 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -48,6 +48,7 @@
#include "ntdll_misc.h"
#include "ddk/wdm.h"
#include "wine/exception.h"
+#include "esync.h"
WINE_DEFAULT_DEBUG_CHANNEL(thread);
@@ -349,6 +350,9 @@ void thread_init(void)
__wine_user_shared_data();
fill_cpu_info();
+ if (do_esync())
+ esync_init();
+
NtCreateKeyedEvent( &keyed_event, GENERIC_READ | GENERIC_WRITE, NULL, 0 );
}
--
2.20.1

View File

@ -0,0 +1,108 @@
From 13a2d3a3d2f281751e7cd6ca252f49a39b82de33 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 15 Jun 2018 17:56:40 -0500
Subject: [PATCH 43/83] ntdll: Lock creating and opening objects with volatile
state.
Note that this also obviates using lock cmpxchg in get_shm(), but I think
it's best to leave that there; we don't really lose anything by it.
---
dlls/ntdll/esync.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index ce62446e6..6bfe24413 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -389,6 +389,15 @@ static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
return ret;
}
+RTL_CRITICAL_SECTION shm_init_section;
+static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
+{
+ 0, 0, &shm_init_section,
+ { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+ 0, 0, { (DWORD_PTR)(__FILE__ ": shm_init_section") }
+};
+RTL_CRITICAL_SECTION shm_init_section = { &critsect_debug, -1, 0, 0, 0, 0 };
+
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
@@ -397,6 +406,15 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
TRACE("name %s, initial %d, max %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
+ /* We need this lock to protect against a potential (though unlikely) race:
+ * if a different process tries to open a named object and manages to use
+ * it between the time we get back from the server and the time we
+ * initialize the shared memory, it'll have uninitialize values for the
+ * object's state. That requires us to be REALLY slow, but we're not taking
+ * any chances. Synchronize on the CS here so that we're sure to be ready
+ * before anyone else can open the object. */
+ RtlEnterCriticalSection( &shm_init_section );
+
ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, EFD_SEMAPHORE );
if (!ret)
{
@@ -409,15 +427,22 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
semaphore->count = initial;
}
+ RtlLeaveCriticalSection( &shm_init_section );
+
return ret;
}
NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
+ NTSTATUS ret;
+
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
+ RtlEnterCriticalSection( &shm_init_section );
+ ret = open_esync( ESYNC_SEMAPHORE, handle, access, attr );
+ RtlLeaveCriticalSection( &shm_init_section );
+ return ret;
}
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
@@ -547,6 +572,8 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
TRACE("name %s, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
+ RtlEnterCriticalSection( &shm_init_section );
+
ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
if (!ret)
{
@@ -557,15 +584,22 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
mutex->count = initial ? 1 : 0;
}
+ RtlLeaveCriticalSection( &shm_init_section );
+
return ret;
}
NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
+ NTSTATUS ret;
+
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- return open_esync( ESYNC_MUTEX, handle, access, attr );
+ RtlEnterCriticalSection( &shm_init_section );
+ ret = open_esync( ESYNC_MUTEX, handle, access, attr );
+ RtlLeaveCriticalSection( &shm_init_section );
+ return ret;
}
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
--
2.20.1

View File

@ -0,0 +1,77 @@
From 20207ace2ee21a22f2dc7095fd70364d54f53127 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 15:08:26 -0500
Subject: [PATCH 44/83] server, ntdll: Pass the shared memory index back from
get_esync_fd.
This should let DuplicateHandle() work completely correctly for semaphores and mutexes.
---
dlls/ntdll/esync.c | 6 +++---
server/esync.c | 7 +++++++
server/protocol.def | 1 +
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 6bfe24413..227dd3df0 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -214,6 +214,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
{
obj_handle_t fd_handle;
enum esync_type type;
+ unsigned int shm_idx;
struct esync *esync;
sigset_t sigset;
NTSTATUS ret;
@@ -231,6 +232,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
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 );
}
@@ -259,9 +261,7 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
esync->fd = fd;
esync->type = type;
- if (type == ESYNC_SEMAPHORE || type == ESYNC_MUTEX)
- FIXME("Attempt to duplicate a semaphore or mutex; this will not work.\n");
- esync->shm = NULL;
+ esync->shm = shm_idx ? get_shm( shm_idx ) : 0;
add_to_list( handle, esync );
diff --git a/server/esync.c b/server/esync.c
index e9a1ec15e..5ef4dd282 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -364,6 +364,13 @@ DECL_HANDLER(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
diff --git a/server/protocol.def b/server/protocol.def
index c2b554490..3037b8161 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4073,6 +4073,7 @@ struct handle_info
obj_handle_t handle; /* handle to the object */
@REPLY
int type; /* esync type (defined below) */
+ unsigned int shm_idx; /* this object's index into the shm section */
@END
enum esync_type
--
2.20.1

View File

@ -0,0 +1,363 @@
From 62de87cd8e8ed0006315080e06c96947727f89b2 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Fri, 15 Jun 2018 14:12:22 -0500
Subject: [PATCH 45/83] server, ntdll: Implement alertable waits.
We do this quite simply by waiting on an extra eventfd descriptor, which the server signals when a user APC is queued.
---
dlls/ntdll/esync.c | 94 +++++++++++++++++++++++++++++++++++------
dlls/ntdll/ntdll_misc.h | 1 +
dlls/ntdll/thread.c | 2 +
server/esync.c | 20 +++++++--
server/esync.h | 1 +
server/protocol.def | 5 +++
server/thread.c | 13 ++++++
server/thread.h | 1 +
8 files changed, 120 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 227dd3df0..583174499 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -684,19 +684,43 @@ static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end )
NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
+ static const LARGE_INTEGER zero = {0};
+
struct esync *objs[MAXIMUM_WAIT_OBJECTS];
- struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 1];
+ struct pollfd fds[MAXIMUM_WAIT_OBJECTS + 2];
int has_esync = 0, has_server = 0;
- DWORD pollcount = count;
BOOL msgwait = FALSE;
LONGLONG timeleft;
LARGE_INTEGER now;
+ DWORD pollcount;
ULONGLONG end;
int64_t value;
ssize_t size;
int i, j;
int 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_section, &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_section, &sigset );
+
+ ntdll_get_thread_data()->esync_apc_fd = fd;
+ }
+
NtQuerySystemTime( &now );
if (timeout)
{
@@ -747,6 +771,8 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
if (msgwait)
DPRINTF(" or driver events (fd %d)", ntdll_get_thread_data()->esync_queue_fd);
+ if (alertable)
+ DPRINTF(", alertable");
if (!timeout)
DPRINTF(", timeout = INFINITE.\n");
@@ -785,10 +811,17 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
}
if (msgwait)
{
- fds[count].fd = ntdll_get_thread_data()->esync_queue_fd;
- fds[count].events = POLLIN;
- pollcount++;
+ fds[i].fd = ntdll_get_thread_data()->esync_queue_fd;
+ fds[i].events = POLLIN;
+ i++;
+ }
+ if (alertable)
+ {
+ fds[i].fd = ntdll_get_thread_data()->esync_apc_fd;
+ fds[i].events = POLLIN;
+ i++;
}
+ pollcount = i;
while (1)
{
@@ -848,10 +881,18 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
}
}
- if (msgwait && (fds[count].revents & POLLIN))
+ if (msgwait)
+ {
+ if (fds[i++].revents & POLLIN)
+ {
+ TRACE("Woken up by driver events.\n");
+ return count - 1;
+ }
+ }
+ if (alertable)
{
- TRACE("Woken up by driver events.\n");
- return count - 1;
+ if (fds[i++].revents & POLLIN)
+ goto userapc;
}
/* If we got here, someone else stole (or reset, etc.) whatever
@@ -890,6 +931,14 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
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];
@@ -905,9 +954,11 @@ tryagain:
continue;
}
- ret = do_poll( fds, 1, timeout ? &end : NULL );
+ 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))
{
@@ -920,9 +971,11 @@ tryagain:
if (msgwait)
{
fds[0].fd = ntdll_get_thread_data()->esync_queue_fd;
- ret = do_poll( fds, 1, timeout ? &end : NULL );
+ ret = do_poll( fds, pollcount, timeout ? &end : NULL );
if (ret <= 0)
goto err;
+ else if (alertable && (fds[1].revents & POLLIN))
+ goto userapc;
}
/* If we got here and we haven't timed out, that means all of the
@@ -934,10 +987,12 @@ tryagain:
}
if (msgwait)
{
- fds[count].fd = ntdll_get_thread_data()->esync_queue_fd;
- fds[count].events = POLLIN;
- pollcount++;
+ fds[i].fd = ntdll_get_thread_data()->esync_queue_fd;
+ fds[i].events = POLLIN;
+ i++;
}
+ /* 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 );
@@ -1018,6 +1073,19 @@ err:
ERR("ppoll failed: %s\n", strerror(errno));
return FILE_GetNtStatus();
}
+
+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_select( 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;
}
NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable,
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index e2ef7d9b8..b6c410c7f 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -281,6 +281,7 @@ struct ntdll_thread_data
{
struct debug_info *debug_info; /* info for debugstr functions */
int esync_queue_fd;/* fd to wait on for driver events */
+ int esync_apc_fd; /* fd to wait on for user APCs */
void *start_stack; /* stack for thread startup */
int request_fd; /* fd for sending server requests */
int reply_fd; /* fd for receiving server replies */
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 04aa8dff8..d146b9e00 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -328,6 +328,7 @@ void thread_init(void)
thread_data->wait_fd[0] = -1;
thread_data->wait_fd[1] = -1;
thread_data->esync_queue_fd = -1;
+ thread_data->esync_apc_fd = -1;
signal_init_thread( teb );
virtual_init_threading();
@@ -700,6 +701,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle_ptr, ACCESS_MASK access, OBJECT
thread_data->wait_fd[1] = -1;
thread_data->start_stack = (char *)teb->Tib.StackBase;
thread_data->esync_queue_fd = -1;
+ thread_data->esync_apc_fd = -1;
pthread_attr_init( &pthread_attr );
pthread_attr_setstack( &pthread_attr, teb->DeallocationStack,
diff --git a/server/esync.c b/server/esync.c
index 5ef4dd282..4fb42e6f9 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -247,19 +247,25 @@ int esync_create_fd( int initval, int flags )
#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 )
{
- static const uint64_t value = 1;
enum esync_type dummy;
int fd;
if (obj->ops->get_esync_fd)
{
fd = obj->ops->get_esync_fd( obj, &dummy );
-
- if (write( fd, &value, sizeof(value) ) == -1)
- perror( "esync: write" );
+ esync_wake_fd( fd );
}
}
@@ -385,3 +391,9 @@ DECL_HANDLER(get_esync_fd)
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
index aeb58c546..cea025d93 100644
--- a/server/esync.h
+++ b/server/esync.h
@@ -21,6 +21,7 @@
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 );
diff --git a/server/protocol.def b/server/protocol.def
index 3037b8161..f55da3a0e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4076,6 +4076,11 @@ struct handle_info
unsigned int shm_idx; /* this object's index into the shm section */
@END
+/* Retrieve the fd to wait on for user APCs. */
+@REQ(get_esync_apc_fd)
+@REPLY
+@END
+
enum esync_type
{
ESYNC_SEMAPHORE = 1,
diff --git a/server/thread.c b/server/thread.c
index 539cd0491..24756d079 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -187,6 +187,7 @@ static inline void init_thread_structure( struct thread *thread )
thread->teb = 0;
thread->entry_point = 0;
thread->esync_fd = -1;
+ thread->esync_apc_fd = -1;
thread->debug_ctx = NULL;
thread->debug_event = NULL;
thread->debug_break = 0;
@@ -295,7 +296,10 @@ 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 );
@@ -1043,8 +1047,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr
grab_object( apc );
list_add_tail( queue, &apc->entry );
if (!list_prev( queue, &apc->entry )) /* first one */
+ {
wake_thread( thread );
+ if (do_esync())
+ esync_wake_fd( thread->esync_apc_fd );
+ }
+
return 1;
}
@@ -1091,6 +1100,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 );
+
return apc;
}
diff --git a/server/thread.h b/server/thread.h
index 4913577f7..1b3bef479 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -55,6 +55,7 @@ struct thread
thread_id_t id; /* thread id */
struct list mutex_list; /* list of currently owned mutexes */
int esync_fd; /* esync file descriptor (signalled on exit) */
+ int esync_apc_fd; /* esync apc fd (signalled when APCs are present) */
struct debug_ctx *debug_ctx; /* debugger context if this thread is a debugger */
struct debug_event *debug_event; /* debug event being sent to debugger */
int debug_break; /* debug breakpoint pending? */
--
2.20.1

View File

@ -0,0 +1,85 @@
From b141d7ebda9f3440e96c8456dfedb83efb51d578 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 10 Jun 2018 13:53:08 -0500
Subject: [PATCH 46/83] esync: Update README.
---
README.esync | 48 +++++++++++++++++-------------------------------
1 file changed, 17 insertions(+), 31 deletions(-)
diff --git a/README.esync b/README.esync
index 8fcb96901..627cc3f48 100644
--- a/README.esync
+++ b/README.esync
@@ -88,32 +88,23 @@ also kind of ugly basically because we have to wait on both X11's fd and the
messages. The upshot about the whole thing was that races are basically
impossible, since a thread can only wait on its own queue.
-I had kind of figured that APCs just wouldn't work, but then poll() spat EINTR
-at me and I realized that this wasn't necessarily true. It seems that the
-server will suspend a thread when trying to deliver a system APC to a thread
-that's not waiting, and since the server has no idea that we're waiting it
-just suspends us. This of course interrupts poll(), which complains at us, and
-it turns out that just returning STATUS_USER_APC in that case is enough to
-make rpcrt4 happy.
+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:
-* We can't return the previous count on a semaphore, since we have no way to
- query the count on a semaphore through eventfd. Currently the code lies and
- returns 1 every time. We can make this work (in a single process, or [if
- necessary] in multiple processes through shared memory) by keeping a count
- locally. We can't guarantee that it's the exact count at the moment the
- semaphore was released, but I guess any program that runs into that race
- shouldn't be depending on that fact anyway.
-* Similarly, we can't enforce the maximum count on a semaphore, since we have
- no way to get the current count and subsequently compare it with the
- maximum.
-* We can't use NtQueryMutant to get the mutant's owner or count if it lives in
- a different process. If necessary we can use shared memory to make this
- work, I guess, but see below.
-* User APCs don't work. However, it's not impossible to make them work; in
- particular I think this could be relatively easily implemented by waiting on
- another internal file descriptor when we execute an alertable wait.
* 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
@@ -144,18 +135,13 @@ surprisingly there aren't that many. In particular:
There are some things that are perfectly implementable but that I just haven't
done yet:
-* NtOpen* (aka Open*). This is just a matter of adding another open_esync
- request analogous to those for other server primitives.
-* NtQuery*. This can be done to some degree (the difficulties are outlined
- above). That said, these APIs aren't exposed through kernel32 in any way, so
+* NtQuery*. That said, these APIs aren't exposed through kernel32 in any way, so
I doubt anyone is going to be using them.
-* SignalObjectAndWait(). The server combines this into a single operation, but
- according to MSDN it doesn't need to be atomic, so we can just signal the
- appropriate object and wait, and woe betide anyone who gets in the way of
- those two operations.
* 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
--
2.20.1

View File

@ -0,0 +1,35 @@
From 5007a124ae988a9913c1e48656f67c3a2dc3b485 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 22:25:40 -0500
Subject: [PATCH 47/83] kernel32/tests: Mark some existing tests as failing
under esync.
---
dlls/kernel32/tests/sync.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index 982a6ae10..befd7cdb5 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -218,7 +218,8 @@ todo_wine
SetLastError(0xdeadbeef);
hOpened = OpenMutexA(GENERIC_READ | GENERIC_WRITE, FALSE, "WineTestMutex");
ok(hOpened != NULL, "OpenMutex failed with error %d\n", GetLastError());
- wait_ret = WaitForSingleObject(hOpened, INFINITE);
+ wait_ret = WaitForSingleObject(hOpened, 0);
+todo_wine_if(getenv("WINEESYNC")) /* XFAIL: validation is not implemented */
ok(wait_ret == WAIT_FAILED, "WaitForSingleObject succeeded\n");
CloseHandle(hOpened);
@@ -249,6 +250,7 @@ todo_wine
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 %d\n", GetLastError());
--
2.20.1

View File

@ -0,0 +1,128 @@
From 5708a3506461e879e20a05292b5826cb89938771 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 22:40:47 -0500
Subject: [PATCH 48/83] kernel32/tests: Add some semaphore tests.
---
dlls/kernel32/tests/sync.c | 98 +++++++++++++++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index befd7cdb5..b8c787d8f 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -577,7 +577,10 @@ static void test_event(void)
static void test_semaphore(void)
{
- HANDLE handle, handle2;
+ HANDLE handle, handle2, handles[2];
+ DWORD ret;
+ LONG prev;
+ int i;
/* test case sensitivity */
@@ -619,6 +622,99 @@ static void test_semaphore(void)
ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError());
CloseHandle( handle );
+
+ handle = CreateSemaphoreA( NULL, 0, 5, NULL );
+ ok(!!handle, "CreateSemaphore failed: %u\n", GetLastError());
+
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ret = ReleaseSemaphore( handle, 1, &prev );
+ ok(ret, "got error %u\n", GetLastError());
+ ok(prev == 0, "got prev %d\n", prev);
+
+ ret = ReleaseSemaphore( handle, 1, &prev );
+ ok(ret, "got error %u\n", GetLastError());
+ ok(prev == 1, "got prev %d\n", prev);
+
+ ret = ReleaseSemaphore( handle, 5, &prev );
+ ok(!ret, "got %d\n", ret);
+ ok(GetLastError() == ERROR_TOO_MANY_POSTS, "got error %u\n", GetLastError());
+ ok(prev == 1, "got prev %d\n", prev);
+
+ ret = ReleaseSemaphore( handle, 2, &prev );
+ ok(ret, "got error %u\n", GetLastError());
+ ok(prev == 2, "got prev %d\n", prev);
+
+ ret = ReleaseSemaphore( handle, 1, &prev );
+ ok(ret, "got error %u\n", GetLastError());
+ ok(prev == 4, "got prev %d\n", prev);
+
+ for (i = 0; i < 5; i++)
+ {
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == 0, "got %u\n", ret);
+ }
+
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ handle2 = CreateSemaphoreA( NULL, 3, 5, NULL );
+ ok(!!handle2, "CreateSemaphore failed: %u\n", GetLastError());
+
+ ret = ReleaseSemaphore( handle2, 1, &prev );
+ ok(ret, "got error %u\n", GetLastError());
+ ok(prev == 3, "got prev %d\n", prev);
+
+ for (i = 0; i < 4; i++)
+ {
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == 0, "got %u\n", ret);
+ }
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ handles[0] = handle;
+ handles[1] = handle2;
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ReleaseSemaphore( handle, 1, NULL );
+ ReleaseSemaphore( handle2, 1, NULL );
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 1, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ReleaseSemaphore( handle, 1, NULL );
+ ReleaseSemaphore( handle2, 1, NULL );
+
+ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ReleaseSemaphore( handle, 1, NULL );
+
+ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = CloseHandle( handle );
+ ok(ret, "got error %u\n", ret);
+
+ ret = CloseHandle( handle2 );
+ ok(ret, "got error %u\n", ret);
}
static void test_waitable_timer(void)
--
2.20.1

View File

@ -0,0 +1,154 @@
From 3e4feaed83e08132a424866386a54b778e1b021b Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 22:59:37 -0500
Subject: [PATCH 49/83] kernel32/tests: Add some event tests.
---
dlls/kernel32/tests/sync.c | 119 ++++++++++++++++++++++++++++++++++++-
1 file changed, 118 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index b8c787d8f..58972a4f2 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -464,12 +464,13 @@ static void test_slist(void)
static void test_event(void)
{
- HANDLE handle, handle2;
+ HANDLE handle, handle2, handles[2];
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
ACL acl;
DWORD ret;
BOOL val;
+ int i;
/* no sd */
handle = CreateEventA(NULL, FALSE, FALSE, __FILE__ ": Test Event");
@@ -573,6 +574,122 @@ static void test_event(void)
ok( ret, "QueryMemoryResourceNotification failed err %u\n", GetLastError() );
ok( val == FALSE || val == TRUE, "wrong value %u\n", val );
CloseHandle( handle );
+
+ handle = CreateEventA( NULL, TRUE, FALSE, NULL );
+ ok(!!handle, "got error %u\n", GetLastError());
+
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ret = SetEvent( handle );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = SetEvent( handle );
+ ok(ret, "got error %u\n", GetLastError());
+
+ for (i = 0; i < 100; i++)
+ {
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == 0, "got %u\n", ret);
+ }
+
+ ret = ResetEvent( handle );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = ResetEvent( handle );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = WaitForSingleObject( handle, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ handle2 = CreateEventA( NULL, FALSE, TRUE, NULL );
+ ok(!!handle2, "got error %u\n", GetLastError());
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ret = SetEvent( handle2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = SetEvent( handle2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = ResetEvent( handle2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = ResetEvent( handle2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ handles[0] = handle;
+ handles[1] = handle2;
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ SetEvent( handle );
+ SetEvent( handle2 );
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ResetEvent( handle );
+ SetEvent( handle2 );
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 1, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ SetEvent( handle );
+ SetEvent( handle2 );
+
+ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ SetEvent( handle2 );
+ ResetEvent( handle );
+
+ ret = WaitForMultipleObjects( 2, handles, TRUE, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ ret = WaitForSingleObject( handle2, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ handles[0] = handle2;
+ handles[1] = handle;
+ SetEvent( handle );
+ SetEvent( handle2 );
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 1, "got %u\n", ret);
+
+ ret = WaitForMultipleObjects( 2, handles, FALSE, 0 );
+ ok(ret == 1, "got %u\n", ret);
+
+ ret = CloseHandle( handle );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = CloseHandle( handle2 );
+ ok(ret, "got error %u\n", GetLastError());
}
static void test_semaphore(void)
--
2.20.1

View File

@ -0,0 +1,126 @@
From fd0423ea32b512123f2b60cb0ebcfc7e4f1cf037 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 23:32:04 -0500
Subject: [PATCH 50/83] kernel32/tests: Add some mutex tests.
---
dlls/kernel32/tests/sync.c | 94 ++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index 58972a4f2..0fb3fd70e 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -177,8 +177,23 @@ static void test_signalandwait(void)
CloseHandle(file);
}
+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 %u, got %u\n", expect, ret);
+
+ if (!ret) ReleaseMutex( mutex );
+ return 0;
+}
+
static void test_mutex(void)
{
+ HANDLE thread;
DWORD wait_ret;
BOOL ret;
HANDLE hCreated;
@@ -289,6 +304,85 @@ todo_wine_if(getenv("WINEESYNC")) /* XFAIL: due to the above */
CloseHandle(hOpened);
CloseHandle(hCreated);
+
+ mutex = CreateMutexA( NULL, FALSE, NULL );
+ ok(!!mutex, "got error %u\n", GetLastError());
+
+ ret = ReleaseMutex( mutex );
+ ok(!ret, "got %d\n", ret);
+ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\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 %u\n", GetLastError());
+ }
+
+ ret = ReleaseMutex( mutex );
+ ok(!ret, "got %d\n", ret);
+ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\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 %u\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 %u\n", GetLastError());
+
+ ret = ReleaseMutex( mutex2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = ReleaseMutex( mutex2 );
+ ok(!ret, "got %d\n", ret);
+ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\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 %u\n", GetLastError());
+
+ ret = ReleaseMutex( mutex2 );
+ ok(!ret, "got %d\n", ret);
+ ok(GetLastError() == ERROR_NOT_OWNER, "got error %u\n", GetLastError());
+
+ ret = WaitForMultipleObjects( 2, mutices, TRUE, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = ReleaseMutex( mutex );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = ReleaseMutex( mutex2 );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = CloseHandle( mutex );
+ ok(ret, "got error %u\n", GetLastError());
+
+ ret = CloseHandle( mutex2 );
+ ok(ret, "got error %u\n", GetLastError());
+
}
static void test_slist(void)
--
2.20.1

View File

@ -0,0 +1,117 @@
From 7dac195394ed70c62bf7c20794e737071b94e0cb Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 13 Jun 2018 23:58:01 -0500
Subject: [PATCH 51/83] kernel32/tests: Add some tests for wait timeouts.
---
dlls/kernel32/tests/sync.c | 68 ++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index 0fb3fd70e..c79e0595e 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -54,6 +54,7 @@ static BOOLEAN (WINAPI *pTryAcquireSRWLockShared)(PSRWLOCK);
static NTSTATUS (WINAPI *pNtAllocateVirtualMemory)(HANDLE, PVOID *, ULONG, 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,
@@ -1480,11 +1481,15 @@ 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);
@@ -1569,6 +1574,68 @@ static void test_WaitForSingleObject(void)
status = pNtWaitForSingleObject(GetCurrentThread(), FALSE, &timeout);
ok(status == STATUS_TIMEOUT, "expected STATUS_TIMEOUT, got %08x\n", status);
+ ret = WaitForSingleObject( signaled, 0 );
+ ok(ret == 0, "got %u\n", ret);
+
+ ret = WaitForSingleObject( nonsignaled, 0 );
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+
+ /* test that a timed wait actually does wait */
+ now = GetTickCount();
+ ret = WaitForSingleObject( nonsignaled, 100 );
+ then = GetTickCount();
+ ok(ret == WAIT_TIMEOUT, "got %u\n", ret);
+ ok(abs((then - now) - 100) < 5, "got %u ms\n", then - now);
+
+ now = GetTickCount();
+ ret = WaitForSingleObject( signaled, 100 );
+ then = GetTickCount();
+ ok(ret == 0, "got %u\n", ret);
+ ok(abs(then - now) < 5, "got %u ms\n", then - now);
+
+ ret = WaitForSingleObject( signaled, INFINITE );
+ ok(ret == 0, "got %u\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 %#x\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 %#x\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 %#x\n", status);
+
+ timeout.QuadPart = TIMEOUT_INFINITE;
+ status = pNtWaitForSingleObject( signaled, FALSE, &timeout );
+ ok(status == 0, "got %#x\n", status);
+
+ pNtQuerySystemTime( &ntnow );
+ timeout.QuadPart = ntnow.QuadPart;
+ status = pNtWaitForSingleObject( nonsignaled, FALSE, &timeout );
+ pNtQuerySystemTime( &ntthen );
+ ok(status == STATUS_TIMEOUT, "got %#x\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 %#x\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);
}
@@ -2963,6 +3030,7 @@ 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");
--
2.20.1

View File

@ -0,0 +1,182 @@
From f0c5814c083c825326b2e3d3bc03e688262f4012 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 19 Jun 2018 10:50:09 -0500
Subject: [PATCH 52/83] ntdll: Go through the server if necessary when
performing event/semaphore/mutex ops.
Because we might have a duplicated or inherited handle, and we won't know about it until we ask the server.
---
dlls/ntdll/esync.c | 50 +++++++++++++++++++++++++++-------------------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 583174499..5eef7e3c8 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -197,7 +197,7 @@ static BOOL add_to_list( HANDLE handle, struct esync *obj )
return TRUE;
}
-static void *esync_get_object( HANDLE handle )
+static struct esync *get_cached_object( HANDLE handle )
{
UINT_PTR entry, idx = handle_to_index( handle, &entry );
@@ -206,11 +206,14 @@ static void *esync_get_object( HANDLE handle )
return esync_list[entry][idx];
}
-/* Gets a waitable object. This is either a proper esync object (i.e. an event,
+/* 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_waitable_object( HANDLE handle, struct esync **obj )
+ * message queue, etc.)
+ *
+ * Note that we have to make the server path available even for esync objects
+ * since we might be passed a duplicated or inherited handle. */
+static NTSTATUS get_object( HANDLE handle, struct esync **obj )
{
obj_handle_t fd_handle;
enum esync_type type;
@@ -220,11 +223,11 @@ static NTSTATUS get_waitable_object( HANDLE handle, struct esync **obj )
NTSTATUS ret;
int fd;
- if ((*obj = esync_get_object( handle ))) return STATUS_SUCCESS;
+ if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS;
/* We need to try grabbing it from the server. */
server_enter_uninterrupted_section( &fd_cache_section, &sigset );
- if (!(esync = esync_get_object( handle )))
+ if (!(esync = get_cached_object( handle )))
{
SERVER_START_REQ( get_esync_fd )
{
@@ -421,7 +424,7 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
/* Initialize the shared memory portion.
* Note we store max here (even though we don't need to) just to keep
* it the same size as the mutex's shm portion. */
- struct esync *obj = esync_get_object( *handle );
+ struct esync *obj = get_cached_object( *handle );
struct semaphore *semaphore = obj->shm;
semaphore->max = max;
semaphore->count = initial;
@@ -447,14 +450,15 @@ NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
{
- struct esync *obj = esync_get_object( handle );
+ struct esync *obj;
struct semaphore *semaphore;
uint64_t count64 = count;
ULONG current;
+ NTSTATUS ret;
TRACE("%p, %d, %p.\n", handle, count, prev);
- if (!obj) return STATUS_INVALID_HANDLE;
+ if ((ret = get_object( handle, &obj))) return ret;
semaphore = obj->shm;
do
@@ -499,12 +503,13 @@ NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
{
- struct esync *obj = esync_get_object( handle );
static const uint64_t value = 1;
+ struct esync *obj;
+ NTSTATUS ret;
TRACE("%p.\n", handle);
- if (!obj) return STATUS_INVALID_HANDLE;
+ if ((ret = get_object( handle, &obj ))) return ret;
if (prev)
{
@@ -520,12 +525,13 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
{
- struct esync *obj = esync_get_object( handle );
static uint64_t value;
+ struct esync *obj;
+ NTSTATUS ret;
TRACE("%p.\n", handle);
- if (!obj) return STATUS_INVALID_HANDLE;
+ if ((ret = get_object( handle, &obj ))) return ret;
if (prev)
{
@@ -541,12 +547,13 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
{
- struct esync *obj = esync_get_object( handle );
static uint64_t value = 1;
+ struct esync *obj;
+ NTSTATUS ret;
TRACE("%p.\n", handle);
- if (!obj) return STATUS_INVALID_HANDLE;
+ if ((ret = get_object( handle, &obj ))) return ret;
if (prev)
{
@@ -578,7 +585,7 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
if (!ret)
{
/* Initialize the shared memory portion. */
- struct esync *obj = esync_get_object( *handle );
+ struct esync *obj = get_cached_object( *handle );
struct mutex *mutex = obj->shm;
mutex->tid = initial ? GetCurrentThreadId() : 0;
mutex->count = initial ? 1 : 0;
@@ -604,13 +611,14 @@ NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
{
- struct esync *obj = esync_get_object( handle );
+ struct esync *obj;
struct mutex *mutex;
static const uint64_t value = 1;
+ NTSTATUS ret;
TRACE("%p, %p.\n", handle, prev);
- if (!obj) return STATUS_INVALID_HANDLE;
+ 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
@@ -734,7 +742,7 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
for (i = 0; i < count; i++)
{
- ret = get_waitable_object( handles[i], &objs[i] );
+ ret = get_object( handles[i], &objs[i] );
if (ret == STATUS_SUCCESS)
has_esync = 1;
else if (ret == STATUS_NOT_IMPLEMENTED)
@@ -1091,10 +1099,10 @@ userapc:
NTSTATUS esync_signal_and_wait( HANDLE signal, HANDLE wait, BOOLEAN alertable,
const LARGE_INTEGER *timeout )
{
- struct esync *obj = esync_get_object( signal );
+ struct esync *obj;
NTSTATUS ret;
- if (!obj) return STATUS_INVALID_HANDLE;
+ if ((ret = get_object( signal, &obj ))) return ret;
switch (obj->type)
{
--
2.20.1

View File

@ -0,0 +1,85 @@
From 3c9b318c047c0ec2397401ea329f59aa64690cf5 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 19 Jun 2018 11:27:58 -0500
Subject: [PATCH 53/83] server: Create eventfd descriptors for
console_input_events objects.
---
server/console.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/server/console.c b/server/console.c
index 059839e06..c32270855 100644
--- a/server/console.c
+++ b/server/console.c
@@ -38,6 +38,7 @@
#include "unicode.h"
#include "wincon.h"
#include "winternl.h"
+#include "esync.h"
struct screen_buffer;
struct console_input_events;
@@ -96,6 +97,7 @@ static const struct object_ops console_input_ops =
static void console_input_events_dump( struct object *obj, int verbose );
static void console_input_events_destroy( struct object *obj );
static int console_input_events_signaled( struct object *obj, struct wait_queue_entry *entry );
+static int console_input_events_get_esync_fd( struct object *obj, enum esync_type *type );
struct console_input_events
{
@@ -103,6 +105,7 @@ struct console_input_events
int num_alloc; /* number of allocated events */
int num_used; /* number of actually used events */
struct console_renderer_event* events;
+ int esync_fd; /* esync file descriptor (signalled when events present) */
};
static const struct object_ops console_input_events_ops =
@@ -113,7 +116,7 @@ static const struct object_ops console_input_events_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
console_input_events_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ console_input_events_get_esync_fd,/* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
@@ -253,6 +256,13 @@ static int console_input_events_signaled( struct object *obj, struct wait_queue_
return (evts->num_used != 0);
}
+static int console_input_events_get_esync_fd( struct object *obj, enum esync_type *type )
+{
+ struct console_input_events *evts = (struct console_input_events *)obj;
+ *type = ESYNC_MANUAL_SERVER;
+ return evts->esync_fd;
+}
+
/* add an event to the console's renderer events list */
static void console_input_events_append( struct console_input* console,
struct console_renderer_event* evt)
@@ -307,6 +317,9 @@ static void console_input_events_get( struct console_input_events* evts )
(evts->num_used - num) * sizeof(evts->events[0]) );
}
evts->num_used -= num;
+
+ if (do_esync() && !evts->num_used)
+ esync_clear( evts->esync_fd );
}
static struct console_input_events *create_console_input_events(void)
@@ -316,6 +329,10 @@ static struct console_input_events *create_console_input_events(void)
if (!(evt = alloc_object( &console_input_events_ops ))) return NULL;
evt->num_alloc = evt->num_used = 0;
evt->events = NULL;
+ evt->esync_fd = -1;
+
+ if (do_esync())
+ evt->esync_fd = esync_create_fd( 0, 0 );
return evt;
}
--
2.20.1

View File

@ -0,0 +1,52 @@
From ac4712c326cdda97942befa72a83142e10969697 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 20 Jun 2018 13:30:45 -0500
Subject: [PATCH 54/83] server: Alter conditions in is_queue_hung(), again.
Complications with SMTO_ABORTIFHUNG lead to implementing this function
exactly correctly.
On Windows, a queue is not hung if you somehow process messages (doesn't
matter how), or if you call MsgWaitForMultipleObjects().
In a sense, Wine had this correct, except not exactly. Firstly, you can call
MsgWait(0, NULL, FALSE, 0, 0); Sleep(1000); in a loop and not be considered
hung; i.e. you don't have to be actively waiting on the queue, but can just
poll it. You also don't have to have a mask that lets in any events--but
just calling WaitForMultipleObjects() isn't enough. On the other hand,
calling MsgWait(0, NULL, FALSE, INFINITE, 0) will result in a hung queue.
last_get_msg should probably be renamed, but nothing better comes to mind.
---
server/queue.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/server/queue.c b/server/queue.c
index e217a5e6a..16ed7c5d3 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -975,7 +975,7 @@ static void cleanup_results( struct msg_queue *queue )
/* check if the thread owning the queue is hung (not checking for messages) */
static int is_queue_hung( struct msg_queue *queue )
{
- return is_signaled( queue ) && (current_time - queue->last_get_msg > 5 * TICKS_PER_SEC);
+ return (current_time - queue->last_get_msg > 5 * TICKS_PER_SEC);
}
static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -991,6 +991,12 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent
}
if (process->idle_event && !(queue->wake_mask & QS_SMRESULT)) set_event( process->idle_event );
+ /* On Windows, we are considered hung iff we have not somehow processed
+ * messages OR done a MsgWait call in the last 5 seconds. Note that in the
+ * latter case repeatedly waiting for 0 seconds is not hung, but waiting
+ * forever is hung, so this is correct. */
+ queue->last_get_msg = current_time;
+
if (queue->fd && list_empty( &obj->wait_queue )) /* first on the queue */
set_fd_events( queue->fd, POLLIN );
add_queue( obj, entry );
--
2.20.1

View File

@ -0,0 +1,40 @@
From 78390efe3e29255fc19abd46e1741a332f6acde9 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 20 Jun 2018 13:41:12 -0500
Subject: [PATCH 55/83] ntdll: Let the server know when we are doing a message
wait.
---
dlls/ntdll/esync.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 5eef7e3c8..d935a4af5 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -753,11 +753,22 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE)
{
+ select_op_t select_op;
+
/* Last object in the list is a queue, which means someone is using
* MsgWaitForMultipleObjects(). We have to wait not only for the server
* fd (signaled on send_message, etc.) but also the USER driver's fd
* (signaled on e.g. X11 events.) */
msgwait = TRUE;
+
+ /* We need to let the server know we are doing a message wait, for two
+ * reasons. First one is WaitForInputIdle(). Second one is checking for
+ * hung queues. Do it like this. */
+ select_op.wait.op = SELECT_WAIT;
+ select_op.wait.handles[0] = wine_server_obj_handle( handles[count - 1] );
+ ret = server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), 0, &zero );
+ if (ret != STATUS_WAIT_0 && ret != STATUS_TIMEOUT)
+ ERR("Unexpected ret %#x\n", ret);
}
if (has_esync && has_server)
--
2.20.1

View File

@ -0,0 +1,35 @@
From e880d8328ae7d2602c828298fcf78e7bc9188add Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 20 Jun 2018 14:04:04 -0500
Subject: [PATCH 56/83] ntdll: Avoid server_select() when waiting for critical
sections.
There's no reason not to always use NtWaitForSingleObject(), so just do that.
And of course this lets esync work right.
---
dlls/ntdll/critsection.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/dlls/ntdll/critsection.c b/dlls/ntdll/critsection.c
index 6b17cebe0..d9add3f06 100644
--- a/dlls/ntdll/critsection.c
+++ b/dlls/ntdll/critsection.c
@@ -220,12 +220,9 @@ static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
{
HANDLE sem = get_semaphore( crit );
LARGE_INTEGER time;
- select_op_t select_op;
time.QuadPart = timeout * (LONGLONG)-10000000;
- select_op.wait.op = SELECT_WAIT;
- select_op.wait.handles[0] = wine_server_obj_handle( sem );
- ret = server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), 0, &time );
+ ret = NtWaitForSingleObject( sem, FALSE, &time );
}
return ret;
}
--
2.20.1

View File

@ -0,0 +1,56 @@
From 9d4292cad821b6eb8a43a5fe122d1acb931b6897 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 20 Jun 2018 15:07:28 -0500
Subject: [PATCH 57/83] user32: Remove hooks that time out.
In accordance with Win7+ behaviour.
---
dlls/user32/hook.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/dlls/user32/hook.c b/dlls/user32/hook.c
index 3aa7b33a8..79171fac0 100644
--- a/dlls/user32/hook.c
+++ b/dlls/user32/hook.c
@@ -381,6 +381,7 @@ void *get_hook_proc( void *proc, const WCHAR *module, HMODULE *free_module )
static LRESULT call_hook( struct hook_info *info, INT code, WPARAM wparam, LPARAM lparam )
{
DWORD_PTR ret = 0;
+ LRESULT lres;
if (info->tid)
{
@@ -394,20 +395,24 @@ static LRESULT call_hook( struct hook_info *info, INT code, WPARAM wparam, LPARA
switch(info->id)
{
case WH_KEYBOARD_LL:
- MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
- wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
- get_ll_hook_timeout(), &ret );
+ lres = MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
+ wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret );
break;
case WH_MOUSE_LL:
- MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
- wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
- get_ll_hook_timeout(), &ret );
+ lres = MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
+ wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret );
break;
default:
ERR("Unknown hook id %d\n", info->id);
assert(0);
break;
}
+
+ if (!lres && GetLastError() == ERROR_TIMEOUT)
+ {
+ TRACE("Hook %p timed out; removing it.\n", info->handle);
+ UnhookWindowsHookEx( info->handle );
+ }
}
else if (info->proc)
{
--
2.20.1

View File

@ -0,0 +1,28 @@
From 6b854028f5c0aa5ae5cdea1e1ee9a9193fd14eb0 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 20 Jun 2018 15:10:08 -0500
Subject: [PATCH 58/83] server: Don't check for a hung queue when sending
low-level hooks.
Since user32 does this.
This logic is independent of the SMTO_ABORTIFHUNG logic on Windows. In fact, IsHungAppWindow() uses yet another algorithm.
---
server/queue.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/server/queue.c b/server/queue.c
index 16ed7c5d3..174a4ac09 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -1688,7 +1688,6 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa
if (!(hook_thread = get_first_global_hook( id ))) return 0;
if (!(queue = hook_thread->queue)) return 0;
- if (is_queue_hung( queue )) return 0;
if (!(msg = mem_alloc( sizeof(*msg) ))) return 0;
--
2.20.1

View File

@ -0,0 +1,108 @@
From 05b8d4c26a8b73a782d5f88de09a7db351af012d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 26 Jun 2018 18:44:44 -0500
Subject: [PATCH 59/83] kernel32/tests: Zigzag test.
The primary function is to check for races. The secondary function is to measure performance.
---
dlls/kernel32/tests/sync.c | 79 ++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index c79e0595e..d790bcfa0 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -3005,6 +3005,84 @@ static void test_apc_deadlock(void)
CloseHandle(pi.hProcess);
}
+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;
@@ -3064,4 +3142,5 @@ START_TEST(sync)
test_srwlock_example();
test_alertable_wait();
test_apc_deadlock();
+ test_zigzag_event();
}
--
2.20.1

View File

@ -0,0 +1,31 @@
From ada3afff191a9781b1109ef4a30b541c9bfeb09d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 1 Jul 2018 12:00:09 +0200
Subject: [PATCH 60/83] =?UTF-8?q?server:=20Try=20to=20remove=20a=20pre?=
=?UTF-8?q?=C3=ABxisting=20shm=20file.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This should have been removed even if wineserver was killed, but if someone
uses SIGKILL or something similar we should try to handle that gracefully.
---
server/esync.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/server/esync.c b/server/esync.c
index 4fb42e6f9..a2f828173 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -84,6 +84,8 @@ void esync_init(void)
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" );
--
2.20.1

View File

@ -0,0 +1,78 @@
From 4bcee007d0380fe04426d68ca090f0300b03ddd0 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 4 Jul 2018 14:34:37 +0200
Subject: [PATCH 61/83] ntdll: Implement NtQuerySemaphore().
---
dlls/ntdll/esync.c | 26 ++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 31 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index d935a4af5..90241289d 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -481,6 +481,32 @@ NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_query_semaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len )
+{
+ struct esync *obj;
+ struct semaphore *semaphore;
+ SEMAPHORE_BASIC_INFORMATION *out = info;
+ NTSTATUS ret;
+
+ TRACE("%p, %u, %p, %u, %p.\n", handle, class, info, len, ret_len);
+
+ if (class != SemaphoreBasicInformation)
+ {
+ FIXME("(%p,%d,%u) Unknown class\n", handle, class, len);
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ 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 )
{
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 88f491e5a..39a4dfbbe 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -39,6 +39,8 @@ extern NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_query_semaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 2f8d6fe5c..95850d7a8 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -305,6 +305,9 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla
NTSTATUS ret;
SEMAPHORE_BASIC_INFORMATION *out = info;
+ if (do_esync())
+ return esync_query_semaphore( handle, class, info, len, ret_len );
+
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
if (class != SemaphoreBasicInformation)
--
2.20.1

View File

@ -0,0 +1,79 @@
From 04d5bb8ddc3e77feb8b14fccbb3f605a35684d38 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 4 Jul 2018 14:40:30 +0200
Subject: [PATCH 62/83] ntdll: Implement NtQueryEvent().
---
dlls/ntdll/esync.c | 27 +++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 32 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 90241289d..62f5e9d97 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -597,6 +597,33 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len )
+{
+ struct esync *obj;
+ EVENT_BASIC_INFORMATION *out = info;
+ struct pollfd fd;
+ NTSTATUS ret;
+
+ TRACE("%p, %u, %p, %u, %p.\n", handle, class, info, len, ret_len);
+
+ if (class != EventBasicInformation)
+ {
+ FIXME("(%p,%d,%u) Unknown class\n", handle, class, len);
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ 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 )
{
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 39a4dfbbe..7af338bbc 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -41,6 +41,8 @@ extern NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_query_semaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 95850d7a8..2256bfc15 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -506,6 +506,9 @@ NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class,
NTSTATUS ret;
EVENT_BASIC_INFORMATION *out = info;
+ if (do_esync())
+ return esync_query_event( handle, class, info, len, ret_len );
+
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
if (class != EventBasicInformation)
--
2.20.1

View File

@ -0,0 +1,79 @@
From c4616c8d0c79387d72a7a242ac1af0fff99e841f Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 4 Jul 2018 14:47:23 +0200
Subject: [PATCH 63/83] ntdll: Implement NtQueryMutant().
---
dlls/ntdll/esync.c | 27 +++++++++++++++++++++++++++
dlls/ntdll/esync.h | 2 ++
dlls/ntdll/sync.c | 3 +++
3 files changed, 32 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 62f5e9d97..0a1999162 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -696,6 +696,33 @@ NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
return STATUS_SUCCESS;
}
+NTSTATUS esync_query_mutex( HANDLE handle, MUTANT_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len )
+{
+ struct esync *obj;
+ struct mutex *mutex;
+ MUTANT_BASIC_INFORMATION *out = info;
+ NTSTATUS ret;
+
+ TRACE("%p, %u, %p, %u, %p.\n", handle, class, info, len, ret_len);
+
+ if (class != MutantBasicInformation)
+ {
+ FIXME("(%p,%d,%u) Unknown class\n", handle, class, len);
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ if ((ret = get_object( handle, &obj ))) return ret;
+ mutex = obj->shm;
+
+ out->CurrentCount = 1 - mutex->count;
+ out->OwnedByCaller = (mutex->tid == GetCurrentThreadId());
+ out->AbandonedState = FALSE;
+ if (ret_len) *ret_len = sizeof(*out);
+
+ return STATUS_SUCCESS;
+}
+
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
diff --git a/dlls/ntdll/esync.h b/dlls/ntdll/esync.h
index 7af338bbc..dd30b255b 100644
--- a/dlls/ntdll/esync.h
+++ b/dlls/ntdll/esync.h
@@ -43,6 +43,8 @@ extern NTSTATUS esync_query_semaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLAS
void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
+extern NTSTATUS esync_query_mutex( HANDLE handle, MUTANT_INFORMATION_CLASS class,
+ void *info, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN;
extern NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
BOOLEAN alertable, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index 2256bfc15..9a809f895 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -629,6 +629,9 @@ NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class,
NTSTATUS ret;
MUTANT_BASIC_INFORMATION *out = info;
+ if (do_esync())
+ return esync_query_mutex( handle, class, info, len, ret_len );
+
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
if (class != MutantBasicInformation)
--
2.20.1

View File

@ -0,0 +1,129 @@
From 735941d777f8ab865de3d192bf2f878e556b9ca4 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 7 Jul 2018 12:57:47 +0200
Subject: [PATCH 64/83] server: Create eventfd descriptors for pseudo-fd
objects and use them for named pipes.
---
server/fd.c | 22 ++++++++++++++++++++++
server/file.h | 1 +
server/named_pipe.c | 4 ++--
3 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/server/fd.c b/server/fd.c
index a855c27d5..9e2f2c2cd 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -101,6 +101,7 @@
#include "handle.h"
#include "process.h"
#include "request.h"
+#include "esync.h"
#include "winternl.h"
#include "winioctl.h"
@@ -195,6 +196,7 @@ struct fd
struct completion *completion; /* completion object attached to this fd */
apc_param_t comp_key; /* completion key to set in completion events */
unsigned int comp_flags; /* completion flags */
+ int esync_fd; /* esync file descriptor */
};
static void fd_dump( struct object *obj, int verbose );
@@ -1500,6 +1502,9 @@ 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 );
}
/* check if the desired access is possible without violating */
@@ -1614,6 +1619,7 @@ static struct fd *alloc_fd_object(void)
fd->poll_index = -1;
fd->completion = NULL;
fd->comp_flags = 0;
+ fd->esync_fd = -1;
init_async_queue( &fd->read_q );
init_async_queue( &fd->write_q );
init_async_queue( &fd->wait_q );
@@ -1651,11 +1657,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
fd->completion = NULL;
fd->comp_flags = 0;
fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
+ fd->esync_fd = -1;
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;
}
@@ -2011,6 +2021,9 @@ 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 );
}
/* check if fd is signaled */
@@ -2048,6 +2061,15 @@ 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 fd *fd = get_obj_fd( obj );
+ int ret = fd->esync_fd;
+ *type = ESYNC_MANUAL_SERVER;
+ release_object( fd );
+ return ret;
+}
+
/* default map_access() routine for objects that behave like an fd */
unsigned int default_fd_map_access( struct object *obj, unsigned int access )
{
diff --git a/server/file.h b/server/file.h
index 25561efc9..2bea2554b 100644
--- a/server/file.h
+++ b/server/file.h
@@ -103,6 +103,7 @@ extern int is_fd_signaled( struct fd *fd );
extern char *dup_fd_name( struct fd *root, const char *name );
extern int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry );
+extern int default_fd_get_esync_fd( struct object *obj, enum esync_type *type );
extern unsigned int default_fd_map_access( struct object *obj, unsigned int access );
extern int default_fd_get_poll_events( struct fd *fd );
extern void default_poll_event( struct fd *fd, int event );
diff --git a/server/named_pipe.c b/server/named_pipe.c
index aa3a8b7b7..dc8b9f635 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -161,7 +161,7 @@ static const struct object_ops pipe_server_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
default_fd_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ default_fd_get_esync_fd, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
pipe_end_get_fd, /* get_fd */
@@ -204,7 +204,7 @@ static const struct object_ops pipe_client_ops =
add_queue, /* add_queue */
remove_queue, /* remove_queue */
default_fd_signaled, /* signaled */
- NULL, /* get_esync_fd */
+ default_fd_get_esync_fd, /* get_esync_fd */
no_satisfied, /* satisfied */
no_signal, /* signal */
pipe_end_get_fd, /* get_fd */
--
2.20.1

View File

@ -0,0 +1,175 @@
From 3e771ee17ee12068b93e4d400477a6c550d1746a Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 7 Jul 2018 15:10:53 +0200
Subject: [PATCH 65/83] ntdll: Cache the esync struct itself instead of a
pointer to it.
Now that the esync object has a constant size, we can perform this
optimization.
---
dlls/ntdll/esync.c | 70 ++++++++++++++++------------------------------
1 file changed, 24 insertions(+), 46 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 0a1999162..3d0c17fd1 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -157,11 +157,11 @@ static void *get_shm( unsigned int idx )
/* 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 128
+#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 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 )
{
@@ -170,7 +170,7 @@ static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry )
return idx % ESYNC_LIST_BLOCK_SIZE;
}
-static BOOL add_to_list( HANDLE handle, struct esync *obj )
+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 );
@@ -192,9 +192,12 @@ static BOOL add_to_list( HANDLE handle, struct esync *obj )
}
}
- obj = interlocked_xchg_ptr((void **)&esync_list[entry][idx], obj);
- assert(!obj);
- return TRUE;
+ if (!interlocked_cmpxchg((int *)&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 )
@@ -202,8 +205,9 @@ 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];
+ return &esync_list[entry][idx];
}
/* Gets an object. This is either a proper esync object (i.e. an event,
@@ -215,19 +219,18 @@ static struct esync *get_cached_object( HANDLE handle )
* since we might be passed a duplicated or inherited handle. */
static NTSTATUS get_object( HANDLE handle, struct esync **obj )
{
+ NTSTATUS ret = STATUS_SUCCESS;
+ enum esync_type type = 0;
+ unsigned int shm_idx = 0;
obj_handle_t fd_handle;
- enum esync_type type;
- unsigned int shm_idx;
- struct esync *esync;
sigset_t sigset;
- NTSTATUS ret;
- int fd;
+ int fd = -1;
if ((*obj = get_cached_object( handle ))) return STATUS_SUCCESS;
/* We need to try grabbing it from the server. */
server_enter_uninterrupted_section( &fd_cache_section, &sigset );
- if (!(esync = get_cached_object( handle )))
+ if (!(*obj = get_cached_object( handle )))
{
SERVER_START_REQ( get_esync_fd )
{
@@ -244,10 +247,9 @@ static NTSTATUS get_object( HANDLE handle, struct esync **obj )
}
server_leave_uninterrupted_section( &fd_cache_section, &sigset );
- if (esync)
+ if (*obj)
{
/* We managed to grab it while in the CS; return it. */
- *obj = esync;
return STATUS_SUCCESS;
}
@@ -260,31 +262,21 @@ static NTSTATUS get_object( HANDLE handle, struct esync **obj )
TRACE("Got fd %d for handle %p.\n", fd, handle);
- esync = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*esync) );
- esync->fd = fd;
- esync->type = type;
-
- esync->shm = shm_idx ? get_shm( shm_idx ) : 0;
-
- add_to_list( handle, esync );
-
- *obj = esync;
+ *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 );
- struct esync *obj;
TRACE("%p.\n", handle);
if (entry < ESYNC_LIST_ENTRIES && esync_list[entry])
{
- if ((obj = interlocked_xchg_ptr( (void **)&esync_list[entry][idx], 0 )))
+ if (interlocked_xchg((int *)&esync_list[entry][idx].type, 0))
{
- close( obj->fd );
- RtlFreeHeap( GetProcessHeap(), 0, obj );
+ close( esync_list[entry][idx].fd );
return STATUS_SUCCESS;
}
}
@@ -330,14 +322,7 @@ static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
- struct esync *obj = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*obj) );
- if (!obj) return STATUS_NO_MEMORY;
-
- obj->type = type;
- obj->fd = fd;
- obj->shm = shm_idx ? get_shm( shm_idx ) : 0;
-
- add_to_list( *handle, obj );
+ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
TRACE("-> handle %p, fd %d, shm index %d.\n", *handle, fd, shm_idx);
}
@@ -378,14 +363,7 @@ static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
if (!ret)
{
- struct esync *obj = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*obj) );
- if (!obj) return STATUS_NO_MEMORY;
-
- obj->type = type;
- obj->fd = fd;
- obj->shm = shm_idx ? get_shm( shm_idx ) : 0;
-
- add_to_list( *handle, obj );
+ add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
TRACE("-> handle %p, fd %d.\n", *handle, fd);
}
--
2.20.1

View File

@ -0,0 +1,55 @@
From 10f5229d4f0740c36e01f2e5ab35faefcec55eaa Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Wed, 4 Jul 2018 14:58:33 +0200
Subject: [PATCH 66/83] esync: Update README.
---
README.esync | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/README.esync b/README.esync
index 627cc3f48..30e241755 100644
--- a/README.esync
+++ b/README.esync
@@ -2,6 +2,29 @@ This is eventfd-based synchronization, or 'esync' for short. Turn it on with
WINEESYNC=1 (note that it checks the presence and not the value); 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.
+
+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.
+
+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
@@ -135,8 +158,6 @@ surprisingly there aren't that many. In particular:
There are some things that are perfectly implementable but that I just haven't
done yet:
-* NtQuery*. That said, these APIs aren't exposed through kernel32 in any way, so
- I doubt anyone is going to be using them.
* 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.
--
2.20.1

View File

@ -0,0 +1,41 @@
From 49055d312f46cb413b1cbbc47d88d64651331ec9 Mon Sep 17 00:00:00 2001
From: Mathieu Comandon <strycore@gmail.com>
Date: Sat, 21 Jul 2018 12:56:50 -0700
Subject: [PATCH 67/83] esync: Add note about file limits not being raised when
using systemd.
---
README.esync | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/README.esync b/README.esync
index 30e241755..7706f395e 100644
--- a/README.esync
+++ b/README.esync
@@ -20,6 +20,16 @@ have a reasonable limit.) To raise the limit you'll want to edit
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=100000
+
+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.
@@ -188,4 +198,4 @@ done by the kernel.
Anyway, yeah, this is esync. Use it if you like.
---Zebediah Figura
\ No newline at end of file
+--Zebediah Figura
--
2.20.1

View File

@ -0,0 +1,29 @@
From c1003dbbc24a76415478209e46988ac0123ef915 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 26 Jul 2018 15:00:02 -0600
Subject: [PATCH 68/83] ntdll: Ignore pseudo-handles.
---
dlls/ntdll/esync.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 3d0c17fd1..506d1c99d 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -228,6 +228,12 @@ static NTSTATUS get_object( HANDLE handle, struct esync **obj )
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;
+ }
+
/* We need to try grabbing it from the server. */
server_enter_uninterrupted_section( &fd_cache_section, &sigset );
if (!(*obj = get_cached_object( handle )))
--
2.20.1

View File

@ -0,0 +1,93 @@
From ecf0f52e7d1e3978aed20e585fe9068f8427d26d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 26 Jul 2018 10:01:54 -0600
Subject: [PATCH 69/83] ntdll: Try to avoid poll() for uncontended objects.
Just semaphores and mutexes thus far.
We don't have to worry about races because this is just a hint: we still call read() eventually.
---
dlls/ntdll/esync.c | 60 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 50 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 506d1c99d..4e5fcef8e 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -869,23 +869,63 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
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 && obj->type == ESYNC_MUTEX)
+ if (obj)
{
- /* If we already own the mutex, return immediately. */
- /* Note: This violates the assumption that the *first* object
- * to be signaled will be returned. If that becomes a problem,
- * we can always check the state of each object before waiting. */
- struct mutex *mutex = obj->shm;
+ switch (obj->type)
+ {
+ case ESYNC_MUTEX:
+ {
+ struct mutex *mutex = obj->shm;
- if (mutex->tid == GetCurrentThreadId())
+ 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))
+ {
+ TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+ mutex->tid = GetCurrentThreadId();
+ mutex->count++;
+ return i;
+ }
+ }
+ break;
+ }
+ case ESYNC_SEMAPHORE:
{
- TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- mutex->count++;
- return i;
+ 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);
+ interlocked_xchg_add( &semaphore->count, -1 );
+ return i;
+ }
+ }
+ break;
+ }
+ case ESYNC_AUTO_EVENT:
+ case ESYNC_MANUAL_EVENT:
+ /* TODO */
+ 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;
}
}
--
2.20.1

View File

@ -0,0 +1,414 @@
From 05b7e550b210c50deb5dbf31a50f96ebe0160c34 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Thu, 26 Jul 2018 11:20:44 -0600
Subject: [PATCH 70/83] ntdll: Store an event's signaled state internally.
This was way harder than I thought it would be...
---
dlls/ntdll/esync.c | 237 +++++++++++++++++++++++++++++++++++----------
server/esync.c | 26 +++--
2 files changed, 195 insertions(+), 68 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 4e5fcef8e..663b3658b 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -93,12 +93,21 @@ 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);
static char shm_name[29];
static int shm_fd;
@@ -396,7 +405,7 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
/* We need this lock to protect against a potential (though unlikely) race:
* if a different process tries to open a named object and manages to use
* it between the time we get back from the server and the time we
- * initialize the shared memory, it'll have uninitialize values for the
+ * initialize the shared memory, it'll have uninitialized values for the
* object's state. That requires us to be REALLY slow, but we're not taking
* any chances. Synchronize on the CS here so that we're sure to be ready
* before anyone else can open the object. */
@@ -495,40 +504,124 @@ 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);
+ NTSTATUS ret;
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 );
+ RtlEnterCriticalSection( &shm_init_section );
+
+ ret = create_esync( type, handle, access, attr, initial, 0 );
+
+ if (!ret)
+ {
+ /* Initialize the shared memory portion. */
+ struct esync *obj = get_cached_object( *handle );
+ struct event *event = obj->shm;
+ event->signaled = initial;
+ event->locked = 0;
+ }
+
+ RtlLeaveCriticalSection( &shm_init_section );
+
+ return ret;
}
NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
+ NTSTATUS ret;
+
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
+ RtlEnterCriticalSection( &shm_init_section );
+ ret = open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
+ RtlLeaveCriticalSection( &shm_init_section );
+ return ret;
+}
+
+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.
+ *
+ * And of course if SetEvent() follows WaitFor() entirely, well, there's no
+ * problem at all.
+ */
+
NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
{
static const uint64_t value = 1;
struct esync *obj;
+ struct event *event;
+ LONG current;
NTSTATUS ret;
- TRACE("%p.\n", handle);
+ TRACE("handle %p, prev %p.\n", handle, prev);
if ((ret = get_object( handle, &obj ))) return ret;
+ event = obj->shm;
+
+ /* Acquire the spinlock. */
+ while (interlocked_cmpxchg( &event->locked, 1, 0 ))
+ small_pause();
- if (prev)
+ /* Only bother signaling the fd if we weren't already signaled. */
+ if (!(current = interlocked_xchg( &event->signaled, 1 )))
{
- FIXME("Can't write previous value.\n");
- *prev = 1;
+ if (write( obj->fd, &value, sizeof(value) ) == -1)
+ return FILE_GetNtStatus();
}
- if (write( obj->fd, &value, sizeof(value) ) == -1)
- return FILE_GetNtStatus();
+ if (prev) *prev = current;
+
+ /* Release the spinlock. */
+ event->locked = 0;
return STATUS_SUCCESS;
}
@@ -537,20 +630,30 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
{
static uint64_t value;
struct esync *obj;
+ struct event *event;
+ LONG current;
NTSTATUS ret;
- TRACE("%p.\n", handle);
+ TRACE("handle %p, prev %p.\n", handle, prev);
if ((ret = get_object( handle, &obj ))) return ret;
+ event = obj->shm;
+
+ /* Acquire the spinlock. */
+ while (interlocked_cmpxchg( &event->locked, 1, 0 ))
+ small_pause();
- if (prev)
+ /* Only bother signaling the fd if we weren't already signaled. */
+ if ((current = interlocked_xchg( &event->signaled, 0 )))
{
- FIXME("Can't write previous value.\n");
- *prev = 1;
+ /* we don't care about the return value */
+ read( obj->fd, &value, sizeof(value) );
}
- /* we don't care about the return value */
- read( obj->fd, &value, sizeof(value) );
+ if (prev) *prev = current;
+
+ /* Release the spinlock. */
+ event->locked = 0;
return STATUS_SUCCESS;
}
@@ -559,17 +662,18 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
{
static uint64_t value = 1;
struct esync *obj;
+ struct event *event;
+ LONG current;
NTSTATUS ret;
TRACE("%p.\n", handle);
if ((ret = get_object( handle, &obj ))) return ret;
+ event = obj->shm;
- if (prev)
- {
- FIXME("Can't write previous value.\n");
- *prev = 1;
- }
+ /* Acquire the spinlock. */
+ while (interlocked_cmpxchg( &event->locked, 1, 0 ))
+ small_pause();
/* This isn't really correct; an application could miss the write.
* Unfortunately we can't really do much better. Fortunately this is rarely
@@ -578,6 +682,12 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
return FILE_GetNtStatus();
read( obj->fd, &value, sizeof(value) );
+ current = interlocked_xchg( &event->signaled, 0 );
+ if (prev) *prev = current;
+
+ /* Release the spinlock. */
+ event->locked = 0;
+
return STATUS_SUCCESS;
}
@@ -751,6 +861,35 @@ static int do_poll( struct pollfd *fds, nfds_t nfds, ULONGLONG *end )
return ret;
}
+static void update_grabbed_object( struct esync *obj )
+{
+ 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). */
+ 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. */
+ interlocked_xchg_add( &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(), for
+ * reasons described near esync_set_event(). */
+ event->signaled = 0;
+ }
+}
+
/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we
* need to delegate to server_select(). */
NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
@@ -916,9 +1055,30 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
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;
+ }
+ }
+ }
case ESYNC_MANUAL_EVENT:
- /* TODO */
+ {
+ 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:
@@ -979,25 +1139,7 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
{
/* We found our object. */
TRACE("Woken up by handle %p [%d].\n", handles[i], i);
- 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). */
- mutex->tid = GetCurrentThreadId();
- mutex->count = 1;
- }
- 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. */
- interlocked_xchg_add( &semaphore->count, -1 );
- }
+ update_grabbed_object( obj );
return i;
}
}
@@ -1161,19 +1303,8 @@ tryagain:
/* Make sure to let ourselves know that we grabbed the mutexes
* and semaphores. */
for (i = 0; i < count; i++)
- {
- if (objs[i]->type == ESYNC_MUTEX)
- {
- struct mutex *mutex = objs[i]->shm;
- mutex->tid = GetCurrentThreadId();
- mutex->count++;
- }
- else if (objs[i]->type == ESYNC_SEMAPHORE)
- {
- struct semaphore *semaphore = objs[i]->shm;
- interlocked_xchg_add( &semaphore->count, -1 );
- }
- }
+ update_grabbed_object( objs[i] );
+
TRACE("Wait successful.\n");
return STATUS_SUCCESS;
}
diff --git a/server/esync.c b/server/esync.c
index a2f828173..847f1e974 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -192,25 +192,21 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
return NULL;
}
esync->type = type;
- if (type == ESYNC_SEMAPHORE || type == ESYNC_MUTEX)
+
+ /* 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)
{
- /* 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 += sysconf( _SC_PAGESIZE );
+ if (ftruncate( shm_fd, shm_size ) == -1)
{
- /* Better expand the shm section. */
- shm_size += sysconf( _SC_PAGESIZE );
- if (ftruncate( shm_fd, shm_size ) == -1)
- {
- fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
- shm_name, shm_size );
- perror( "ftruncate" );
- }
+ fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
+ shm_name, shm_size );
+ perror( "ftruncate" );
}
}
- else
- esync->shm_idx = 0;
}
else
{
--
2.20.1

View File

@ -0,0 +1,32 @@
From 332a9c67d3d400e6d127afce26f9e52114c50d5c Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 28 Jul 2018 12:09:40 -0500
Subject: [PATCH 71/83] ntdll: Fix growing the shm_addrs array.
Fixes an issue exposed by the last patch and visible in GTA V. Presumably
this didn't show up earlier because applications usually don't create large
numbers of semaphores or mutexes, and previously events didn't use shared
memory.
---
dlls/ntdll/esync.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 663b3658b..a94a5aa6e 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -143,8 +143,9 @@ static void *get_shm( unsigned int idx )
if (entry >= shm_addrs_size)
{
- shm_addrs_size *= 2;
- if (!(shm_addrs = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, shm_addrs, shm_addrs_size )))
+ shm_addrs_size = entry + 1;
+ if (!(shm_addrs = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
+ shm_addrs, shm_addrs_size * sizeof(shm_addrs[0]) )))
ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size);
}
--
2.20.1

View File

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

View File

@ -0,0 +1,25 @@
From bcf741e2f08c2e571083796efef1aeab36d3daee Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 31 Jul 2018 11:54:39 -0500
Subject: [PATCH 73/83] ntdll: Fix a missing break statement.
Should fix #2, #3, #6.
---
dlls/ntdll/esync.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index a94a5aa6e..3ab069da9 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -1068,6 +1068,7 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
return i;
}
}
+ break;
}
case ESYNC_MANUAL_EVENT:
{
--
2.20.1

View File

@ -0,0 +1,102 @@
From 6887c3ae33f8304d5a038c7f9d2dac5d29ad8821 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 4 Aug 2018 15:15:12 -0500
Subject: [PATCH 74/83] ntdll, server: Abort if esync is enabled for the server
but not the client, and vice versa.
---
dlls/ntdll/esync.c | 28 +++++++++++++++++++++++++++-
dlls/ntdll/thread.c | 3 +--
server/esync.c | 12 ++++++++++++
3 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 3ab069da9..1dd96c8e6 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -115,10 +115,29 @@ static void **shm_addrs;
static int shm_addrs_size; /* length of the allocated shm_addrs array */
static long pagesize;
+static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags );
+
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( wine_get_config_dir(), &st ) == -1)
ERR("Cannot stat %s\n", wine_get_config_dir());
@@ -128,7 +147,14 @@ void esync_init(void)
sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino );
if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1)
- ERR("Failed to initialize shared memory: %s\n", strerror( errno ));
+ {
+ /* 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 );
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index d146b9e00..2c8777e13 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -351,8 +351,7 @@ void thread_init(void)
__wine_user_shared_data();
fill_cpu_info();
- if (do_esync())
- esync_init();
+ esync_init();
NtCreateKeyedEvent( &keyed_event, GENERIC_READ | GENERIC_WRITE, NULL, 0 );
}
diff --git a/server/esync.c b/server/esync.c
index 1fc9316b1..cb4de2daa 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -395,6 +395,18 @@ DECL_HANDLER(create_esync)
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_4 );
+ return;
+ }
+
if (!objattr) return;
if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->flags, req->type, sd )))
--
2.20.1

View File

@ -0,0 +1,52 @@
From 930e29146eaee56a2e0a4aa1c122717e96ff1ec8 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 4 Aug 2018 15:18:24 -0500
Subject: [PATCH 75/83] esync: Update README.
---
README.esync | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/README.esync b/README.esync
index 7706f395e..b64bfefc1 100644
--- a/README.esync
+++ b/README.esync
@@ -5,7 +5,7 @@ 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.
++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
@@ -20,11 +20,11 @@ have a reasonable limit.) To raise the limit you'll want to edit
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
+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=100000
+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
@@ -157,10 +157,6 @@ surprisingly there aren't that many. In particular:
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.
-* We might hit the maximum number of open fd's. On my system the soft limit is
- 1024 and the hard limit is 1048576. I'm inclined to hope this won't be an
- issue, since a hypothetical Linux port of any application might just as well
- use the same number of eventfds.
* 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
--
2.20.1

View File

@ -0,0 +1,28 @@
From e66bb0ab91a9b83822edb9c1d3471c603848a3ab Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 11 Aug 2018 10:45:43 -0500
Subject: [PATCH 76/83] ntdll: Correctly allocate the esync handle cache.
Fixes a regression introduced by 7b583a367ca05f961c1183a9647245ecd9f2160f.
Should hopefully fix #7.
---
dlls/ntdll/esync.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 1dd96c8e6..53a93f3cf 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -221,7 +221,7 @@ static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, v
if (!entry) esync_list[0] = esync_list_initial_block;
else
{
- void *ptr = wine_anon_mmap( NULL, ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync *),
+ void *ptr = wine_anon_mmap( NULL, ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync),
PROT_READ | PROT_WRITE, 0 );
if (ptr == MAP_FAILED) return FALSE;
esync_list[entry] = ptr;
--
2.20.1

View File

@ -0,0 +1,153 @@
From d743c7745d1fcdf53f9575bf903a6af88b23f776 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 29 Jul 2018 16:53:58 -0500
Subject: [PATCH 77/83] ntdll, server: Specify EFD_SEMAPHORE on the server
side.
This just makes things cleaner; since we already pass the type to the server
there's no reason to pass this as well.
---
dlls/ntdll/esync.c | 20 ++++++--------------
server/esync.c | 11 ++++++++---
server/protocol.def | 1 -
3 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 53a93f3cf..4f418b211 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -33,9 +33,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
-#ifdef HAVE_SYS_EVENTFD_H
-# include <sys/eventfd.h>
-#endif
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
@@ -52,10 +49,6 @@
#include "ntdll_misc.h"
#include "esync.h"
-#ifndef EFD_SEMAPHORE
-#define EFD_SEMAPHORE 1
-#endif
-
WINE_DEFAULT_DEBUG_CHANNEL(esync);
int do_esync(void)
@@ -116,7 +109,7 @@ static int shm_addrs_size; /* length of the allocated shm_addrs array */
static long pagesize;
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
- ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags );
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval );
void esync_init(void)
{
@@ -128,7 +121,7 @@ void esync_init(void)
HANDLE handle;
NTSTATUS ret;
- ret = create_esync( 0, &handle, 0, NULL, 0, 0 );
+ ret = create_esync( 0, &handle, 0, NULL, 0 );
if (ret != STATUS_NOT_IMPLEMENTED)
{
ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n");
@@ -327,7 +320,7 @@ NTSTATUS esync_close( HANDLE handle )
}
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
- ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int flags )
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval )
{
NTSTATUS ret;
data_size_t len;
@@ -346,7 +339,6 @@ static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
{
req->access = access;
req->initval = initval;
- req->flags = flags;
req->type = type;
wine_server_add_data( req, objattr, len );
ret = wine_server_call( req );
@@ -438,7 +430,7 @@ NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
* before anyone else can open the object. */
RtlEnterCriticalSection( &shm_init_section );
- ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, EFD_SEMAPHORE );
+ ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial );
if (!ret)
{
/* Initialize the shared memory portion.
@@ -539,7 +531,7 @@ NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
RtlEnterCriticalSection( &shm_init_section );
- ret = create_esync( type, handle, access, attr, initial, 0 );
+ ret = create_esync( type, handle, access, attr, initial );
if (!ret)
{
@@ -755,7 +747,7 @@ NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
RtlEnterCriticalSection( &shm_init_section );
- ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
+ ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1 );
if (!ret)
{
/* Initialize the shared memory portion. */
diff --git a/server/esync.c b/server/esync.c
index cb4de2daa..f5556e91c 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -180,7 +180,7 @@ static int type_matches( enum esync_type type1, enum esync_type type2 )
}
static struct esync *create_esync( struct object *root, const struct unicode_str *name,
- unsigned int attr, int initval, int flags, enum esync_type type,
+ unsigned int attr, int initval, enum esync_type type,
const struct security_descriptor *sd )
{
#ifdef HAVE_SYS_EVENTFD_H
@@ -190,8 +190,13 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
{
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 | EFD_CLOEXEC | EFD_NONBLOCK );
+ esync->fd = eventfd( initval, flags );
if (esync->fd == -1)
{
perror( "eventfd" );
@@ -409,7 +414,7 @@ DECL_HANDLER(create_esync)
if (!objattr) return;
- if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->flags, req->type, sd )))
+ if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->type, sd )))
{
if (get_error() == STATUS_OBJECT_NAME_EXISTS)
reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );
diff --git a/server/protocol.def b/server/protocol.def
index f55da3a0e..220c7c407 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4046,7 +4046,6 @@ struct handle_info
@REQ(create_esync)
unsigned int access; /* wanted access rights */
int initval; /* initial value */
- int flags; /* flags (EFD_SEMAPHORE or 0) */
int type; /* type of esync object (see below) */
VARARG(objattr,object_attributes); /* object attributes */
@REPLY
--
2.20.1

View File

@ -0,0 +1,384 @@
From 943e5341b06b05129e2f909b48e446141a6d976d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 29 Jul 2018 16:50:08 -0500
Subject: [PATCH 78/83] ntdll, server: Initialize the shared memory portion on
the server side.
Simply using a CS only prevents this race within one process.
---
dlls/ntdll/esync.c | 100 ++++----------------------------
server/esync.c | 135 +++++++++++++++++++++++++++++---------------
server/protocol.def | 1 +
3 files changed, 101 insertions(+), 135 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 4f418b211..b84b7b83c 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -109,7 +109,7 @@ static int shm_addrs_size; /* length of the allocated shm_addrs array */
static long pagesize;
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
- ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval );
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max );
void esync_init(void)
{
@@ -121,7 +121,7 @@ void esync_init(void)
HANDLE handle;
NTSTATUS ret;
- ret = create_esync( 0, &handle, 0, NULL, 0 );
+ 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");
@@ -320,7 +320,7 @@ NTSTATUS esync_close( HANDLE handle )
}
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
- ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval )
+ ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max )
{
NTSTATUS ret;
data_size_t len;
@@ -340,6 +340,7 @@ static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
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)
@@ -404,60 +405,21 @@ static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
return ret;
}
-RTL_CRITICAL_SECTION shm_init_section;
-static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
-{
- 0, 0, &shm_init_section,
- { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
- 0, 0, { (DWORD_PTR)(__FILE__ ": shm_init_section") }
-};
-RTL_CRITICAL_SECTION shm_init_section = { &critsect_debug, -1, 0, 0, 0, 0 };
-
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
- NTSTATUS ret;
-
TRACE("name %s, initial %d, max %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
- /* We need this lock to protect against a potential (though unlikely) race:
- * if a different process tries to open a named object and manages to use
- * it between the time we get back from the server and the time we
- * initialize the shared memory, it'll have uninitialized values for the
- * object's state. That requires us to be REALLY slow, but we're not taking
- * any chances. Synchronize on the CS here so that we're sure to be ready
- * before anyone else can open the object. */
- RtlEnterCriticalSection( &shm_init_section );
-
- ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial );
- if (!ret)
- {
- /* Initialize the shared memory portion.
- * Note we store max here (even though we don't need to) just to keep
- * it the same size as the mutex's shm portion. */
- struct esync *obj = get_cached_object( *handle );
- struct semaphore *semaphore = obj->shm;
- semaphore->max = max;
- semaphore->count = initial;
- }
-
- RtlLeaveCriticalSection( &shm_init_section );
-
- return ret;
+ return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max );
}
NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
- NTSTATUS ret;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- RtlEnterCriticalSection( &shm_init_section );
- ret = open_esync( ESYNC_SEMAPHORE, handle, access, attr );
- RtlLeaveCriticalSection( &shm_init_section );
- return ret;
+ return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
}
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
@@ -523,41 +485,20 @@ 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);
- NTSTATUS ret;
TRACE("name %s, %s-reset, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>",
event_type == NotificationEvent ? "manual" : "auto", initial);
- RtlEnterCriticalSection( &shm_init_section );
-
- ret = create_esync( type, handle, access, attr, initial );
-
- if (!ret)
- {
- /* Initialize the shared memory portion. */
- struct esync *obj = get_cached_object( *handle );
- struct event *event = obj->shm;
- event->signaled = initial;
- event->locked = 0;
- }
-
- RtlLeaveCriticalSection( &shm_init_section );
-
- return ret;
+ return create_esync( type, handle, access, attr, initial, 0 );
}
NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr )
{
- NTSTATUS ret;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- RtlEnterCriticalSection( &shm_init_section );
- ret = open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
- RtlLeaveCriticalSection( &shm_init_section );
- return ret;
+ return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
}
static inline void small_pause(void)
@@ -740,39 +681,18 @@ NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
{
- NTSTATUS ret;
-
TRACE("name %s, initial %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
- RtlEnterCriticalSection( &shm_init_section );
-
- ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1 );
- if (!ret)
- {
- /* Initialize the shared memory portion. */
- struct esync *obj = get_cached_object( *handle );
- struct mutex *mutex = obj->shm;
- mutex->tid = initial ? GetCurrentThreadId() : 0;
- mutex->count = initial ? 1 : 0;
- }
-
- RtlLeaveCriticalSection( &shm_init_section );
-
- return ret;
+ 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 )
{
- NTSTATUS ret;
-
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
- RtlEnterCriticalSection( &shm_init_section );
- ret = open_esync( ESYNC_MUTEX, handle, access, attr );
- RtlLeaveCriticalSection( &shm_init_section );
- return ret;
+ return open_esync( ESYNC_MUTEX, handle, access, attr );
}
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
diff --git a/server/esync.c b/server/esync.c
index f5556e91c..19e614a89 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -179,8 +179,63 @@ static int type_matches( enum esync_type type1, enum esync_type type2 )
(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)
+ {
+ if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
+ fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
+
+ memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
+
+ shm_addrs_size = entry + 1;
+ }
+
+ if (!shm_addrs[entry])
+ {
+ void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
+ if (addr == (void *)-1)
+ {
+ fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
+ perror( "mmap" );
+ }
+
+ if (debug_level)
+ fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
+
+ if (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
+ munmap( addr, pagesize ); /* someone beat us to it */
+ }
+
+ return (void *)((unsigned long)shm_addrs[entry] + offset);
+}
+
+struct 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);
+
static struct esync *create_esync( struct object *root, const struct unicode_str *name,
- unsigned int attr, int initval, enum esync_type type,
+ unsigned int attr, int initval, int max, enum esync_type type,
const struct security_descriptor *sd )
{
#ifdef HAVE_SYS_EVENTFD_H
@@ -220,6 +275,38 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
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;
+ break;
+ }
+ default:
+ assert( 0 );
+ }
}
else
{
@@ -288,49 +375,6 @@ void esync_clear( int fd )
read( fd, &value, sizeof(value) );
}
-/* Sadly, we need all of this infrastructure to keep the shm state in sync. */
-
-static void *get_shm( unsigned int idx )
-{
- int entry = (idx * 8) / pagesize;
- int offset = (idx * 8) % pagesize;
-
- if (entry >= shm_addrs_size)
- {
- if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
- fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
-
- memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
-
- shm_addrs_size = entry + 1;
- }
-
- if (!shm_addrs[entry])
- {
- void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
- if (addr == (void *)-1)
- {
- fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
- perror( "mmap" );
- }
-
- if (debug_level)
- fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
-
- if (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
- munmap( addr, pagesize ); /* someone beat us to it */
- }
-
- return (void *)((unsigned long)shm_addrs[entry] + offset);
-}
-
-struct event
-{
- int signaled;
- int locked;
-};
-C_ASSERT(sizeof(struct event) == 8);
-
static inline void small_pause(void)
{
#ifdef __i386__
@@ -414,7 +458,8 @@ DECL_HANDLER(create_esync)
if (!objattr) return;
- if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->type, sd )))
+ 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 );
diff --git a/server/protocol.def b/server/protocol.def
index 220c7c407..dbb8770df 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4047,6 +4047,7 @@ struct handle_info
unsigned int access; /* wanted access rights */
int initval; /* initial value */
int type; /* type of esync object (see below) */
+ int max; /* maximum count on a semaphore */
VARARG(objattr,object_attributes); /* object attributes */
@REPLY
obj_handle_t handle; /* handle to the object */
--
2.20.1

View File

@ -0,0 +1,197 @@
From 055edbb9f2cdf23160fa9be1d7f597ec9935c19c Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Mon, 13 Aug 2018 21:35:06 -0500
Subject: [PATCH 79/83] ntdll, server: Revert to old implementation of hung
queue detection.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
By manually notifying the server every time we enter and exit a message wait.
The hung queue logic keeps breaking. In the case of bug #9 it was breaking
because we were waiting for more than 5 seconds on our queue and then someone
sent us a message with SMTO_ABORTIFHUNG. Just stop fighting against the
server and try to coöperate with it instead. It takes two extra server calls,
but ideally the GUI thread isn't going to be in the same sort of performance-
critical code that this patchset was written for.
---
dlls/ntdll/esync.c | 53 ++++++++++++++++++++++++++++++++++-----------
server/protocol.def | 6 ++++-
server/queue.c | 35 ++++++++++++++++++++++++------
3 files changed, 73 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index b84b7b83c..501175c1d 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -831,8 +831,8 @@ static void update_grabbed_object( struct esync *obj )
/* A value of STATUS_NOT_IMPLEMENTED returned from this function means that we
* need to delegate to server_select(). */
-NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any,
- BOOLEAN alertable, const LARGE_INTEGER *timeout )
+static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
+ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
static const LARGE_INTEGER zero = {0};
@@ -895,22 +895,11 @@ NTSTATUS esync_wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_an
if (objs[count - 1] && objs[count - 1]->type == ESYNC_QUEUE)
{
- select_op_t select_op;
-
/* Last object in the list is a queue, which means someone is using
* MsgWaitForMultipleObjects(). We have to wait not only for the server
* fd (signaled on send_message, etc.) but also the USER driver's fd
* (signaled on e.g. X11 events.) */
msgwait = TRUE;
-
- /* We need to let the server know we are doing a message wait, for two
- * reasons. First one is WaitForInputIdle(). Second one is checking for
- * hung queues. Do it like this. */
- select_op.wait.op = SELECT_WAIT;
- select_op.wait.handles[0] = wine_server_obj_handle( handles[count - 1] );
- ret = server_select( &select_op, offsetof( select_op_t, wait.handles[1] ), 0, &zero );
- if (ret != STATUS_WAIT_0 && ret != STATUS_TIMEOUT)
- ERR("Unexpected ret %#x\n", ret);
}
if (has_esync && has_server)
@@ -1282,6 +1271,44 @@ userapc:
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 (!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 )
{
diff --git a/server/protocol.def b/server/protocol.def
index dbb8770df..fb7f1ad1e 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4078,7 +4078,11 @@ struct handle_info
/* Retrieve the fd to wait on for user APCs. */
@REQ(get_esync_apc_fd)
-@REPLY
+@END
+
+/* Notify the server that we are doing a message wait (or done with one). */
+@REQ(esync_msgwait)
+ int in_msgwait; /* are we in a message wait? */
@END
enum esync_type
diff --git a/server/queue.c b/server/queue.c
index 174a4ac09..56fa4d98c 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -126,6 +126,7 @@ struct msg_queue
struct thread *thread; /* reference to the thread owning the queue */
struct fd *fd; /* optional file descriptor to poll */
int esync_fd; /* esync file descriptor (signalled on message) */
+ int esync_in_msgwait; /* our thread is currently waiting on us */
unsigned int wake_bits; /* wakeup bits */
unsigned int wake_mask; /* wakeup mask */
unsigned int changed_bits; /* changed wakeup bits */
@@ -975,7 +976,21 @@ static void cleanup_results( struct msg_queue *queue )
/* check if the thread owning the queue is hung (not checking for messages) */
static int is_queue_hung( struct msg_queue *queue )
{
- return (current_time - queue->last_get_msg > 5 * TICKS_PER_SEC);
+ struct wait_queue_entry *entry;
+
+ if (current_time - queue->last_get_msg <= 5 * TICKS_PER_SEC)
+ return 0; /* less than 5 seconds since last get message -> not hung */
+
+ LIST_FOR_EACH_ENTRY( entry, &queue->obj.wait_queue, struct wait_queue_entry, entry )
+ {
+ if (get_wait_queue_thread(entry)->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 */
+
+ return 1;
}
static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *entry )
@@ -991,12 +1006,6 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent
}
if (process->idle_event && !(queue->wake_mask & QS_SMRESULT)) set_event( process->idle_event );
- /* On Windows, we are considered hung iff we have not somehow processed
- * messages OR done a MsgWait call in the last 5 seconds. Note that in the
- * latter case repeatedly waiting for 0 seconds is not hung, but waiting
- * forever is hung, so this is correct. */
- queue->last_get_msg = current_time;
-
if (queue->fd && list_empty( &obj->wait_queue )) /* first on the queue */
set_fd_events( queue->fd, POLLIN );
add_queue( obj, entry );
@@ -1688,6 +1697,7 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa
if (!(hook_thread = get_first_global_hook( id ))) return 0;
if (!(queue = hook_thread->queue)) return 0;
+ if (is_queue_hung( queue )) return 0;
if (!(msg = mem_alloc( sizeof(*msg) ))) return 0;
@@ -3289,3 +3299,14 @@ DECL_HANDLER(update_rawinput_devices)
e = find_rawinput_device( 1, 6 );
current->process->rawinput_kbd = e ? &e->device : NULL;
}
+
+DECL_HANDLER(esync_msgwait)
+{
+ struct msg_queue *queue = get_current_queue();
+
+ if (!queue) return;
+ queue->esync_in_msgwait = req->in_msgwait;
+
+ if (current->process->idle_event && !(queue->wake_mask & QS_SMRESULT))
+ set_event( current->process->idle_event );
+}
--
2.20.1

View File

@ -0,0 +1,37 @@
From 5f1c5d0a6ab0171bee767562ada00b04ceb98596 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Tue, 14 Aug 2018 20:00:54 -0500
Subject: [PATCH 80/83] ntdll: Fix a couple of misplaced global variables.
There's no reason these should be global, and in particular, this means that esync_pulse_event() might end up writing 0, which raises the likelihood of a missed wakeup from "probable" to "certain".
Fixes #10.
---
dlls/ntdll/esync.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 501175c1d..597cb8fa8 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -588,7 +588,7 @@ NTSTATUS esync_set_event( HANDLE handle, LONG *prev )
NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
{
- static uint64_t value;
+ uint64_t value;
struct esync *obj;
struct event *event;
LONG current;
@@ -620,7 +620,7 @@ NTSTATUS esync_reset_event( HANDLE handle, LONG *prev )
NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
{
- static uint64_t value = 1;
+ uint64_t value = 1;
struct esync *obj;
struct event *event;
LONG current;
--
2.20.1

View File

@ -0,0 +1,29 @@
From 25b0902552205d767139b8eea4e448faa8b67fec Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 19 Aug 2018 12:50:57 -0500
Subject: [PATCH 81/83] ntdll: Yield during PulseEvent().
May help with #10, although the real fix there is just not to use esync.
---
dlls/ntdll/esync.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 597cb8fa8..d9b5b1a70 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -640,6 +640,11 @@ NTSTATUS esync_pulse_event( HANDLE handle, LONG *prev )
* used (and publicly deprecated). */
if (write( obj->fd, &value, sizeof(value) ) == -1)
return FILE_GetNtStatus();
+
+ /* Try to give other threads a chance to wake up. Hopefully erring on this
+ * side is the better thing to do... */
+ NtYieldExecution();
+
read( obj->fd, &value, sizeof(value) );
current = interlocked_xchg( &event->signaled, 0 );
--
2.20.1

View File

@ -0,0 +1,41 @@
From 785a0e353a59eeea347c5e3543e2e6c6af456c6b Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 19 Aug 2018 12:54:33 -0500
Subject: [PATCH 82/83] ntdll, server: Check the value of WINEESYNC instead of
just the presence.
People keep getting tripped up by this.
---
dlls/ntdll/esync.c | 2 +-
server/esync.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index d9b5b1a70..776fc484d 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -57,7 +57,7 @@ int do_esync(void)
static int do_esync_cached = -1;
if (do_esync_cached == -1)
- do_esync_cached = (getenv("WINEESYNC") != NULL);
+ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
return do_esync_cached;
#else
diff --git a/server/esync.c b/server/esync.c
index 19e614a89..4521993d4 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -53,7 +53,7 @@ int do_esync(void)
static int do_esync_cached = -1;
if (do_esync_cached == -1)
- do_esync_cached = (getenv("WINEESYNC") != NULL);
+ do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
return do_esync_cached;
#else
--
2.20.1

View File

@ -0,0 +1,24 @@
From 3f08e4af150adc7e08c15a6836ca6bb8c71746ee Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 19 Aug 2018 13:40:05 -0500
Subject: [PATCH 83/83] esync: Update README.
---
README.esync | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.esync b/README.esync
index b64bfefc1..11d86563a 100644
--- a/README.esync
+++ b/README.esync
@@ -1,6 +1,5 @@
This is eventfd-based synchronization, or 'esync' for short. Turn it on with
-WINEESYNC=1 (note that it checks the presence and not the value); debug it
-with +esync.
+WINEESYNC=1; debug it with +esync.
== BUGS AND LIMITATIONS ==
--
2.20.1

View File

@ -0,0 +1,10 @@
Fixes: [36692] Many multi-threaded applications have poor performance due to heavy use of synchronization primitives
# Note: the following dependencies are essentially artificial; in particular
# these patches do not make use of the Staging shared memory infrastructure.
Depends: server-Shared_Memory
Depends: ntdll-NtSuspendProcess
Depends: ntdll-SystemRoot_Symlink
Depends: ws2_32-WSACleanup
Depends: ntdll-RtlCreateUserThread
Depends: server-Realtime_Priority
Depends: ntdll-User_Shared_Data

File diff suppressed because it is too large Load Diff