Commit 1d38539b authored by Zebediah Figura's avatar Zebediah Figura Committed by Vitaly Lipatov

ntdll: Create eventfd-based objects for semaphores.

parent 3297b7ac
......@@ -46,6 +46,7 @@ C_SRCS = \
unix/cdrom.c \
unix/debug.c \
unix/env.c \
unix/esync.c \
unix/file.c \
unix/loader.c \
unix/loadorder.c \
......
/*
* eventfd-based synchronization objects
*
* Copyright (C) 2018 Zebediah Figura
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/mman.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#define NONAMELESSUNION
#include "windef.h"
#include "winternl.h"
#include "wine/server.h"
#include "wine/debug.h"
#include "unix_private.h"
#include "esync.h"
WINE_DEFAULT_DEBUG_CHANNEL(esync);
int do_esync(void)
{
#ifdef HAVE_SYS_EVENTFD_H
static int do_esync_cached = -1;
if (do_esync_cached == -1)
do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
return do_esync_cached;
#else
static int once;
if (!once++)
FIXME("eventfd not supported on this platform.\n");
return 0;
#endif
}
struct esync
{
enum esync_type type;
int fd;
void *shm;
};
struct semaphore
{
int max;
int count;
};
C_ASSERT(sizeof(struct semaphore) == 8);
static char shm_name[29];
static int shm_fd;
static void **shm_addrs;
static int shm_addrs_size; /* length of the allocated shm_addrs array */
static long pagesize;
static pthread_mutex_t shm_addrs_mutex = PTHREAD_MUTEX_INITIALIZER;
static void *get_shm( unsigned int idx )
{
int entry = (idx * 8) / pagesize;
int offset = (idx * 8) % pagesize;
void *ret;
pthread_mutex_lock( &shm_addrs_mutex );
if (entry >= shm_addrs_size)
{
int new_size = max(shm_addrs_size * 2, entry + 1);
if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) )))
ERR("Failed to grow shm_addrs array to size %d.\n", shm_addrs_size);
memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) );
shm_addrs_size = new_size;
}
if (!shm_addrs[entry])
{
void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
if (addr == (void *)-1)
ERR("Failed to map page %d (offset %#lx).\n", entry, entry * pagesize);
TRACE("Mapping page %d at %p.\n", entry, addr);
if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 ))
munmap( addr, pagesize ); /* someone beat us to it */
}
ret = (void *)((unsigned long)shm_addrs[entry] + offset);
pthread_mutex_unlock( &shm_addrs_mutex );
return ret;
}
/* We'd like lookup to be fast. To that end, we use a static list indexed by handle.
* This is copied and adapted from the fd cache code. */
#define ESYNC_LIST_BLOCK_SIZE (65536 / sizeof(struct esync))
#define ESYNC_LIST_ENTRIES 256
static struct esync *esync_list[ESYNC_LIST_ENTRIES];
static struct esync esync_list_initial_block[ESYNC_LIST_BLOCK_SIZE];
static inline UINT_PTR handle_to_index( HANDLE handle, UINT_PTR *entry )
{
UINT_PTR idx = (((UINT_PTR)handle) >> 2) - 1;
*entry = idx / ESYNC_LIST_BLOCK_SIZE;
return idx % ESYNC_LIST_BLOCK_SIZE;
}
static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, void *shm )
{
UINT_PTR entry, idx = handle_to_index( handle, &entry );
if (entry >= ESYNC_LIST_ENTRIES)
{
FIXME( "too many allocated handles, not caching %p\n", handle );
return FALSE;
}
if (!esync_list[entry]) /* do we need to allocate a new block of entries? */
{
if (!entry) esync_list[0] = esync_list_initial_block;
else
{
void *ptr = anon_mmap_alloc( ESYNC_LIST_BLOCK_SIZE * sizeof(struct esync),
PROT_READ | PROT_WRITE );
if (ptr == MAP_FAILED) return FALSE;
esync_list[entry] = ptr;
}
}
if (!InterlockedCompareExchange( (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 NTSTATUS create_esync( enum esync_type type, HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, int initval, int max )
{
NTSTATUS ret;
data_size_t len;
struct object_attributes *objattr;
obj_handle_t fd_handle;
unsigned int shm_idx;
sigset_t sigset;
int fd;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
/* We have to synchronize on the fd cache CS so that our calls to
* receive_fd don't race with theirs. */
server_enter_uninterrupted_section( &fd_cache_mutex, &sigset );
SERVER_START_REQ( create_esync )
{
req->access = access;
req->initval = initval;
req->type = type;
req->max = max;
wine_server_add_data( req, objattr, len );
ret = wine_server_call( req );
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
*handle = wine_server_ptr_handle( reply->handle );
type = reply->type;
shm_idx = reply->shm_idx;
fd = receive_fd( &fd_handle );
assert( wine_server_ptr_handle(fd_handle) == *handle );
}
}
SERVER_END_REQ;
server_leave_uninterrupted_section( &fd_cache_mutex, &sigset );
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
{
add_to_list( *handle, type, fd, shm_idx ? get_shm( shm_idx ) : 0 );
TRACE("-> handle %p, fd %d.\n", *handle, fd);
}
free( objattr );
return ret;
}
extern NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
{
TRACE("name %s, initial %d, max %d.\n",
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max );
}
void esync_init(void)
{
struct stat st;
if (!do_esync())
{
/* make sure the server isn't running with WINEESYNC */
HANDLE handle;
NTSTATUS ret;
ret = create_esync( 0, &handle, 0, NULL, 0, 0 );
if (ret != STATUS_NOT_IMPLEMENTED)
{
ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n");
exit(1);
}
return;
}
if (stat( config_dir, &st ) == -1)
ERR("Cannot stat %s\n", config_dir);
if (st.st_ino != (unsigned long)st.st_ino)
sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino );
else
sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino );
if ((shm_fd = shm_open( shm_name, O_RDWR, 0644 )) == -1)
{
/* probably the server isn't running with WINEESYNC, tell the user and bail */
if (errno == ENOENT)
ERR("Failed to open esync shared memory file; make sure no stale wineserver instances are running without WINEESYNC.\n");
else
ERR("Failed to initialize shared memory: %s\n", strerror( errno ));
exit(1);
}
pagesize = sysconf( _SC_PAGESIZE );
shm_addrs = calloc( 128, sizeof(shm_addrs[0]) );
shm_addrs_size = 128;
}
/*
* 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 void esync_init(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 mutex so that our calls to receive_fd
* don't race with theirs. It looks weird, I know.
*
* If we weren't trying to avoid touching the code I'd rename the mutex to
* "server_fd_mutex" or something similar. */
extern pthread_mutex_t fd_cache_mutex;
extern int receive_fd( obj_handle_t *handle ) DECLSPEC_HIDDEN;
......@@ -89,6 +89,7 @@
#include "winioctl.h"
#include "winternl.h"
#include "unix_private.h"
#include "esync.h"
#include "wine/list.h"
#include "wine/debug.h"
......@@ -2160,6 +2161,7 @@ static void start_main_thread(void)
signal_init_thread( teb );
dbg_init();
startup_info_size = server_init_process();
esync_init();
virtual_map_user_shared_data();
init_cpu_info();
init_files();
......
......@@ -106,7 +106,7 @@ 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 int initial_cwd = -1;
static pid_t server_pid;
static pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t fd_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
/* atomically exchange a 64-bit value */
static inline LONG64 interlocked_xchg64( LONG64 *dest, LONG64 val )
......@@ -807,7 +807,7 @@ void 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;
......
......@@ -64,6 +64,7 @@
#include "wine/server.h"
#include "wine/debug.h"
#include "unix_private.h"
#include "esync.h"
WINE_DEFAULT_DEBUG_CHANNEL(sync);
......@@ -273,6 +274,9 @@ NTSTATUS WINAPI NtCreateSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJ
if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER;
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
if (do_esync())
return esync_create_semaphore( handle, access, attr, initial, max );
SERVER_START_REQ( create_semaphore )
{
req->access = access;
......
......@@ -41,6 +41,7 @@
#include "handle.h"
#include "request.h"
#include "file.h"
#include "esync.h"
int do_esync(void)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment