Commit 3297b7ac authored by Zebediah Figura's avatar Zebediah Figura Committed by Vitaly Lipatov

server: Create server objects for eventfd-based synchronization objects.

parent 82673ab4
......@@ -5460,7 +5460,6 @@ struct resume_process_reply
};
struct get_next_thread_request
{
struct request_header __header;
......@@ -5477,6 +5476,37 @@ struct get_next_thread_reply
char __pad_12[4];
};
enum esync_type
{
ESYNC_SEMAPHORE = 1,
ESYNC_AUTO_EVENT,
ESYNC_MANUAL_EVENT,
ESYNC_MUTEX,
ESYNC_AUTO_SERVER,
ESYNC_MANUAL_SERVER,
ESYNC_QUEUE,
};
struct create_esync_request
{
struct request_header __header;
unsigned int access;
int initval;
int type;
int max;
/* VARARG(objattr,object_attributes); */
char __pad_28[4];
};
struct create_esync_reply
{
struct reply_header __header;
obj_handle_t handle;
int type;
unsigned int shm_idx;
char __pad_20[4];
};
enum request
{
......@@ -5757,6 +5787,7 @@ enum request
REQ_suspend_process,
REQ_resume_process,
REQ_get_next_thread,
REQ_create_esync,
REQ_NB_REQUESTS
};
......@@ -6041,6 +6072,7 @@ union generic_request
struct suspend_process_request suspend_process_request;
struct resume_process_request resume_process_request;
struct get_next_thread_request get_next_thread_request;
struct create_esync_request create_esync_request;
};
union generic_reply
{
......@@ -6323,11 +6355,12 @@ union generic_reply
struct suspend_process_reply suspend_process_reply;
struct resume_process_reply resume_process_reply;
struct get_next_thread_reply get_next_thread_reply;
struct create_esync_reply create_esync_reply;
};
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 757
#define SERVER_PROTOCOL_VERSION 758
/* ### protocol_version end ### */
......
......@@ -11,6 +11,7 @@ C_SRCS = \
debugger.c \
device.c \
directory.c \
esync.c \
event.c \
fd.c \
file.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
*/
#include "config.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef HAVE_SYS_EVENTFD_H
# include <sys/eventfd.h>
#endif
#include <sys/mman.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <unistd.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "handle.h"
#include "request.h"
#include "file.h"
int do_esync(void)
{
#ifdef HAVE_SYS_EVENTFD_H
static int do_esync_cached = -1;
if (do_esync_cached == -1)
do_esync_cached = getenv("WINEESYNC") && atoi(getenv("WINEESYNC"));
return do_esync_cached;
#else
return 0;
#endif
}
static char shm_name[29];
static int shm_fd;
static off_t shm_size;
static void **shm_addrs;
static int shm_addrs_size; /* length of the allocated shm_addrs array */
static long pagesize;
static void shm_cleanup(void)
{
close( shm_fd );
if (shm_unlink( shm_name ) == -1)
perror( "shm_unlink" );
}
void esync_init(void)
{
struct stat st;
if (fstat( config_dir_fd, &st ) == -1)
fatal_error( "cannot stat config dir\n" );
if (st.st_ino != (unsigned long)st.st_ino)
sprintf( shm_name, "/wine-%lx%08lx-esync", (unsigned long)((unsigned long long)st.st_ino >> 32), (unsigned long)st.st_ino );
else
sprintf( shm_name, "/wine-%lx-esync", (unsigned long)st.st_ino );
shm_unlink( shm_name );
shm_fd = shm_open( shm_name, O_RDWR | O_CREAT | O_EXCL, 0644 );
if (shm_fd == -1)
perror( "shm_open" );
pagesize = sysconf( _SC_PAGESIZE );
shm_addrs = calloc( 128, sizeof(shm_addrs[0]) );
shm_addrs_size = 128;
shm_size = pagesize;
if (ftruncate( shm_fd, shm_size ) == -1)
perror( "ftruncate" );
fprintf( stderr, "esync: up and running.\n" );
atexit( shm_cleanup );
}
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 );
static void esync_destroy( struct object *obj );
static const struct object_ops esync_ops =
{
sizeof(struct esync), /* size */
&no_type, /* type */
esync_dump, /* dump */
no_add_queue, /* add_queue */
NULL, /* remove_queue */
NULL, /* signaled */
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 */
default_get_full_name, /* get_full_name */
no_lookup_name, /* lookup_name */
directory_link_name, /* link_name */
default_unlink_name, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
no_close_handle, /* close_handle */
esync_destroy /* destroy */
};
static void esync_dump( struct object *obj, int verbose )
{
struct esync *esync = (struct esync *)obj;
assert( obj->ops == &esync_ops );
fprintf( stderr, "esync fd=%d\n", esync->fd );
}
static void esync_destroy( struct object *obj )
{
struct esync *esync = (struct esync *)obj;
close( esync->fd );
}
static int type_matches( enum esync_type type1, enum esync_type type2 )
{
return (type1 == type2) ||
((type1 == ESYNC_AUTO_EVENT || type1 == ESYNC_MANUAL_EVENT) &&
(type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT));
}
static void *get_shm( unsigned int idx )
{
int entry = (idx * 8) / pagesize;
int offset = (idx * 8) % pagesize;
if (entry >= shm_addrs_size)
{
int new_size = max(shm_addrs_size * 2, entry + 1);
if (!(shm_addrs = realloc( shm_addrs, new_size * sizeof(shm_addrs[0]) )))
fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
memset( shm_addrs + shm_addrs_size, 0, (new_size - shm_addrs_size) * sizeof(shm_addrs[0]) );
shm_addrs_size = new_size;
}
if (!shm_addrs[entry])
{
void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
if (addr == (void *)-1)
{
fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
perror( "mmap" );
}
if (debug_level)
fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
if (__sync_val_compare_and_swap( &shm_addrs[entry], 0, addr ))
munmap( addr, pagesize ); /* someone beat us to it */
}
return (void *)((unsigned long)shm_addrs[entry] + offset);
}
struct semaphore
{
int max;
int count;
};
C_ASSERT(sizeof(struct semaphore) == 8);
struct esync *create_esync( struct object *root, const struct unicode_str *name,
unsigned int attr, int initval, int max, enum esync_type type,
const struct security_descriptor *sd )
{
#ifdef HAVE_SYS_EVENTFD_H
struct esync *esync;
if ((esync = create_named_object( root, &esync_ops, name, attr, sd )))
{
if (get_error() != STATUS_OBJECT_NAME_EXISTS)
{
int flags = EFD_CLOEXEC | EFD_NONBLOCK;
if (type == ESYNC_SEMAPHORE)
flags |= EFD_SEMAPHORE;
/* initialize it if it didn't already exist */
esync->fd = eventfd( initval, flags );
if (esync->fd == -1)
{
perror( "eventfd" );
file_set_error();
release_object( esync );
return NULL;
}
esync->type = type;
/* Use the fd as index, since that'll be unique across all
* processes, but should hopefully end up also allowing reuse. */
esync->shm_idx = esync->fd + 1; /* we keep index 0 reserved */
while (esync->shm_idx * 8 >= shm_size)
{
/* Better expand the shm section. */
shm_size += pagesize;
if (ftruncate( shm_fd, shm_size ) == -1)
{
fprintf( stderr, "esync: couldn't expand %s to size %ld: ",
shm_name, (long)shm_size );
perror( "ftruncate" );
}
}
/* Initialize the shared memory portion. We want to do this on the
* server side to avoid a potential though unlikely race whereby
* the same object is opened and used between the time it's created
* and the time its shared memory portion is initialized. */
switch (type)
{
case ESYNC_SEMAPHORE:
{
struct semaphore *semaphore = get_shm( esync->shm_idx );
semaphore->max = max;
semaphore->count = initval;
break;
}
default:
assert( 0 );
}
}
else
{
/* validate the type */
if (!type_matches( type, esync->type ))
{
release_object( &esync->obj );
set_error( STATUS_OBJECT_TYPE_MISMATCH );
return NULL;
}
}
}
return esync;
#else
/* FIXME: Provide a fallback implementation using pipe(). */
set_error( STATUS_NOT_IMPLEMENTED );
return NULL;
#endif
}
DECL_HANDLER(create_esync)
{
struct esync *esync;
struct unicode_str name;
struct object *root;
const struct security_descriptor *sd;
const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root );
if (!do_esync())
{
set_error( STATUS_NOT_IMPLEMENTED );
return;
}
if (!req->type)
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (!objattr) return;
if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->max, req->type, sd )))
{
if (get_error() == STATUS_OBJECT_NAME_EXISTS)
reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );
else
reply->handle = alloc_handle_no_access_check( current->process, esync,
req->access, objattr->attributes );
reply->type = esync->type;
reply->shm_idx = esync->shm_idx;
send_client_fd( current->process, esync->fd, reply->handle );
release_object( esync );
}
if (root) release_object( root );
}
/*
* eventfd-based synchronization objects
*
* Copyright (C) 2018 Zebediah Figura
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <unistd.h>
extern int do_esync(void);
void esync_init(void);
......@@ -34,6 +34,7 @@
#include "thread.h"
#include "request.h"
#include "unicode.h"
#include "esync.h"
/* command-line options */
int debug_level = 0;
......@@ -229,6 +230,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() );
set_current_time();
init_scheduler();
......
......@@ -3755,7 +3755,6 @@ struct handle_info
obj_handle_t handle; /* process handle */
@END
/* Iterate thread list for process */
@REQ(get_next_thread)
obj_handle_t process; /* process handle */
......@@ -3766,3 +3765,27 @@ struct handle_info
@REPLY
obj_handle_t handle; /* next thread handle */
@END
enum esync_type
{
ESYNC_SEMAPHORE = 1,
ESYNC_AUTO_EVENT,
ESYNC_MANUAL_EVENT,
ESYNC_MUTEX,
ESYNC_AUTO_SERVER,
ESYNC_MANUAL_SERVER,
ESYNC_QUEUE,
};
/* Create a new eventfd-based synchronization object */
@REQ(create_esync)
unsigned int access; /* wanted access rights */
int initval; /* initial value */
int type; /* type of esync object */
int max; /* maximum count on a semaphore */
VARARG(objattr,object_attributes); /* object attributes */
@REPLY
obj_handle_t handle; /* handle to the object */
int type; /* actual type (may be different for events) */
unsigned int shm_idx;
@END
......@@ -396,6 +396,7 @@ DECL_HANDLER(terminate_job);
DECL_HANDLER(suspend_process);
DECL_HANDLER(resume_process);
DECL_HANDLER(get_next_thread);
DECL_HANDLER(create_esync);
#ifdef WANT_REQUEST_HANDLERS
......@@ -679,6 +680,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_suspend_process,
(req_handler)req_resume_process,
(req_handler)req_get_next_thread,
(req_handler)req_create_esync,
};
C_ASSERT( sizeof(abstime_t) == 8 );
......@@ -2268,6 +2270,15 @@ C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 );
C_ASSERT( sizeof(struct get_next_thread_request) == 32 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 );
C_ASSERT( sizeof(struct get_next_thread_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct create_esync_request, access) == 12 );
C_ASSERT( FIELD_OFFSET(struct create_esync_request, initval) == 16 );
C_ASSERT( FIELD_OFFSET(struct create_esync_request, type) == 20 );
C_ASSERT( FIELD_OFFSET(struct create_esync_request, max) == 24 );
C_ASSERT( sizeof(struct create_esync_request) == 32 );
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, handle) == 8 );
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, type) == 12 );
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, shm_idx) == 16 );
C_ASSERT( sizeof(struct create_esync_reply) == 24 );
#endif /* WANT_REQUEST_HANDLERS */
......
......@@ -4500,6 +4500,22 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req
fprintf( stderr, " handle=%04x", req->handle );
}
static void dump_create_esync_request( const struct create_esync_request *req )
{
fprintf( stderr, " access=%08x", req->access );
fprintf( stderr, ", initval=%d", req->initval );
fprintf( stderr, ", type=%d", req->type );
fprintf( stderr, ", max=%d", req->max );
dump_varargs_object_attributes( ", objattr=", cur_size );
}
static void dump_create_esync_reply( const struct create_esync_reply *req )
{
fprintf( stderr, " handle=%04x", req->handle );
fprintf( stderr, ", type=%d", req->type );
fprintf( stderr, ", shm_idx=%08x", req->shm_idx );
}
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_get_new_process_info_request,
......@@ -4778,6 +4794,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_suspend_process_request,
(dump_func)dump_resume_process_request,
(dump_func)dump_get_next_thread_request,
(dump_func)dump_create_esync_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
......@@ -5058,6 +5075,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
NULL,
NULL,
(dump_func)dump_get_next_thread_reply,
(dump_func)dump_create_esync_reply,
};
static const char * const req_names[REQ_NB_REQUESTS] = {
......@@ -5338,6 +5356,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"suspend_process",
"resume_process",
"get_next_thread",
"create_esync",
};
static const struct
......
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