This will be useful if not necessary at some point for shared resources, to send / receive fds with the D3DKMT requests.
There's maybe an option to use `wine_server_fd_to_handle` / `wine_server_handle_to_fd` instead, but that would require creating file objects for every resource on the server side, and that seemed a bit unnecessary.
Also, on the macOS side, we will likely want to extend these helper and the fd_type enum, to include and support sending / receiving mach_port_t fd types, as shared resources are backed by those on that platform. This probably doesn't fit too well with the otherwise unix file fds and the fd cache.
It's maybe a bit awkward to squeeze the fd type bits into the request header but I didn't find a good other way and increasing its size breaks the size <= 64 assumption. It could be a pointer within the request struct passed to the helper call, but it seem a bit awkward from a usage point of view: hard to make it clear that it needs to be within the request struct.
From: Rémi Bernon rbernon@codeweavers.com
--- server/thread.c | 66 +++++++++++++++++++------------------------------ server/thread.h | 2 +- 2 files changed, 27 insertions(+), 41 deletions(-)
diff --git a/server/thread.c b/server/thread.c index 64cd1a74702..93b77f6b36e 100644 --- a/server/thread.c +++ b/server/thread.c @@ -391,6 +391,12 @@ static void apply_thread_priority( struct thread *thread, int effective_priority
#endif
+static void clear_inflight_fd( struct inflight_fd *fd, int need_close ) +{ + if (need_close) close( fd->server ); + fd->client = fd->server = -1; +} + /* initialize the structure for a newly allocated thread */ static inline void init_thread_structure( struct thread *thread ) { @@ -435,8 +441,7 @@ static inline void init_thread_structure( struct thread *thread ) list_init( &thread->user_apc ); list_init( &thread->kernel_object );
- for (i = 0; i < MAX_INFLIGHT_FDS; i++) - thread->inflight[i].server = thread->inflight[i].client = -1; + for (i = 0; i < MAX_INFLIGHT_FDS; i++) clear_inflight_fd( thread->inflight + i, 0 ); }
static inline int is_thread_suspended( struct thread *thread ) @@ -624,14 +629,7 @@ static void cleanup_thread( struct thread *thread ) destroy_thread_windows( thread ); free_msg_queue( thread ); release_thread_desktop( thread, 1 ); - for (i = 0; i < MAX_INFLIGHT_FDS; i++) - { - if (thread->inflight[i].client != -1) - { - close( thread->inflight[i].server ); - thread->inflight[i].client = thread->inflight[i].server = -1; - } - } + for (i = 0; i < MAX_INFLIGHT_FDS; i++) clear_inflight_fd( thread->inflight + i, 1 ); free( thread->desc ); thread->req_data = NULL; thread->reply_data = NULL; @@ -1506,56 +1504,44 @@ static void clear_apc_queue( struct list *queue ) }
/* add an fd to the inflight list */ -/* return list index, or -1 on error */ -int thread_add_inflight_fd( struct thread *thread, int client, int server ) +void thread_add_inflight_fd( struct thread *thread, int client, int server ) { - int i; + struct inflight_fd *fd, new = { .client = client, .server = server };
- if (server == -1) return -1; - if (client == -1) - { - close( server ); - return -1; - } + if (client == -1) clear_inflight_fd( &new, 1 ); + if (server == -1) return;
/* first check if we already have an entry for this fd */ - for (i = 0; i < MAX_INFLIGHT_FDS; i++) - if (thread->inflight[i].client == client) - { - close( thread->inflight[i].server ); - thread->inflight[i].server = server; - return i; - } + for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) + if (fd->client == new.client) goto done;
/* now find a free spot to store it */ - for (i = 0; i < MAX_INFLIGHT_FDS; i++) - if (thread->inflight[i].client == -1) - { - thread->inflight[i].client = client; - thread->inflight[i].server = server; - return i; - } + for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) + if (fd->client == -1) goto done;
- close( server ); - return -1; + fatal_error( "%04x: inflight fd array is full\n", thread->id ); + +done: + clear_inflight_fd( fd, 1 ); + *fd = new; }
/* get an inflight fd and purge it from the list */ /* the fd must be closed when no longer used */ int thread_get_inflight_fd( struct thread *thread, int client ) { - int i, ret; + struct inflight_fd *fd;
if (client == -1) return -1;
do { - for (i = 0; i < MAX_INFLIGHT_FDS; i++) + for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) { - if (thread->inflight[i].client == client) + if (fd->client == client) { - ret = thread->inflight[i].server; - thread->inflight[i].server = thread->inflight[i].client = -1; + int ret = fd->server; + clear_inflight_fd( fd, 0 ); return ret; } } diff --git a/server/thread.h b/server/thread.h index b33a00d9f26..cf9bdf02e65 100644 --- a/server/thread.h +++ b/server/thread.h @@ -123,7 +123,7 @@ extern void kill_thread( struct thread *thread, int violent_death ); extern void wake_up( struct object *obj, int max ); extern int thread_queue_apc( struct process *process, struct thread *thread, struct object *owner, const union apc_call *call_data ); extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type ); -extern int thread_add_inflight_fd( struct thread *thread, int client, int server ); +extern void thread_add_inflight_fd( struct thread *thread, int client, int server ); extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct token *thread_get_impersonation_token( struct thread *thread ); extern unsigned int set_thread_priority( struct thread *thread, int priority );
From: Rémi Bernon rbernon@codeweavers.com
--- server/fd.c | 2 +- server/file.c | 2 +- server/inproc_sync.c | 2 +- server/process.c | 4 ++-- server/protocol.def | 6 ++++++ server/request.c | 30 +++++++++++++++++++++++++++--- server/request.h | 4 ++-- server/thread.c | 40 ++++++++++++++++++++++++++-------------- server/thread.h | 9 +++++---- 9 files changed, 71 insertions(+), 28 deletions(-)
diff --git a/server/fd.c b/server/fd.c index 1b932cc3ae3..fcd6ac85674 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2893,7 +2893,7 @@ DECL_HANDLER(get_handle_fd) reply->type = fd->fd_ops->get_fd_type( fd ); reply->options = fd->options; reply->access = get_handle_access( current->process, req->handle ); - send_client_fd( current->process, unix_fd, req->handle ); + send_client_fd( current->process, FD_TYPE_UNIX, unix_fd, req->handle ); } release_object( fd ); } diff --git a/server/file.c b/server/file.c index cc5acc2aadc..c9dc2c07485 100644 --- a/server/file.c +++ b/server/file.c @@ -680,7 +680,7 @@ DECL_HANDLER(alloc_file_handle) int fd;
reply->handle = 0; - if ((fd = thread_get_inflight_fd( current, req->fd )) == -1) + if ((fd = thread_get_inflight_fd( current, FD_TYPE_UNIX, req->fd )) == -1) { set_error( STATUS_INVALID_HANDLE ); return; diff --git a/server/inproc_sync.c b/server/inproc_sync.c index f20410c85ed..eff2ed47f19 100644 --- a/server/inproc_sync.c +++ b/server/inproc_sync.c @@ -184,7 +184,7 @@ DECL_HANDLER(get_inproc_sync_fd) reply->access = get_handle_access( current->process, req->handle );
if ((fd = get_inproc_sync_fd( obj, &reply->type )) < 0) set_error( STATUS_NOT_IMPLEMENTED ); - else send_client_fd( current->process, fd, req->handle ); + else send_client_fd( current->process, FD_TYPE_UNIX, fd, req->handle );
release_object( obj ); } diff --git a/server/process.c b/server/process.c index 8e0f9189a61..ec1ce0967ad 100644 --- a/server/process.c +++ b/server/process.c @@ -881,7 +881,7 @@ static void process_poll_event( struct fd *fd, int event ) assert( process->obj.ops == &process_ops );
if (event & (POLLERR | POLLHUP)) kill_process( process, !process->is_terminating ); - else if (event & POLLIN) receive_fd( process ); + else if (event & POLLIN) receive_fd( process, FD_TYPE_UNIX ); }
static void startup_info_destroy( struct object *obj ) @@ -1160,7 +1160,7 @@ DECL_HANDLER(new_process) struct debug_obj *debug_obj = NULL; struct process *parent; struct thread *parent_thread = current; - int socket_fd = thread_get_inflight_fd( current, req->socket_fd ); + int socket_fd = thread_get_inflight_fd( current, FD_TYPE_UNIX, req->socket_fd ); const obj_handle_t *handles = NULL; const obj_handle_t *job_handles = NULL; unsigned int i, job_handle_count; diff --git a/server/protocol.def b/server/protocol.def index 59c6436fa88..8966deeb78e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -61,6 +61,12 @@ struct reply_header data_size_t reply_size; /* reply variable part size */ };
+enum fd_type +{ + FD_TYPE_NONE = 0, + FD_TYPE_UNIX = 1, +}; + /* placeholder structure for the maximum allowed request size */ /* this is used to construct the generic_request union */ struct request_max_size diff --git a/server/request.c b/server/request.c index 835ea30cec3..da941c9b59d 100644 --- a/server/request.c +++ b/server/request.c @@ -374,7 +374,7 @@ error: }
/* receive a file descriptor on the process socket */ -int receive_fd( struct process *process ) +static int receive_unix_fd( struct process *process ) { struct iovec vec; struct send_fd data; @@ -424,7 +424,7 @@ int receive_fd( struct process *process ) if (debug_level) fprintf( stderr, "%04x: *fd* %d <- %d\n", thread->id, data.fd, fd ); - thread_add_inflight_fd( thread, data.fd, fd ); + thread_add_inflight_fd( thread, FD_TYPE_UNIX, data.fd, fd ); } if (thread) release_object( thread ); return 0; @@ -454,7 +454,7 @@ int receive_fd( struct process *process ) }
/* send an fd to a client */ -int send_client_fd( struct process *process, int fd, obj_handle_t handle ) +static int send_unix_fd( struct process *process, int fd, obj_handle_t handle ) { struct iovec vec; struct msghdr msghdr; @@ -505,6 +505,30 @@ int send_client_fd( struct process *process, int fd, obj_handle_t handle ) return -1; }
+int receive_fd( struct process *process, enum fd_type type ) +{ + switch (type) + { + case FD_TYPE_NONE: return 0; + case FD_TYPE_UNIX: return receive_unix_fd( process ); + default: assert( 0 ); break; + } +} + +int send_client_fd( struct process *process, enum fd_type type, int fd, obj_handle_t handle ) +{ + int ret = -1; + + switch (type) + { + case FD_TYPE_NONE: ret = 0; break; + case FD_TYPE_UNIX: ret = send_unix_fd( process, fd, handle ); break; + default: assert( 0 ); break; + } + + return ret; +} + /* return a monotonic time counter */ timeout_t monotonic_counter(void) { diff --git a/server/request.h b/server/request.h index 13254d967ed..9d9d106d54a 100644 --- a/server/request.h +++ b/server/request.h @@ -50,8 +50,8 @@ extern const struct object_attributes *get_req_object_attributes( const struct s struct unicode_str *name, struct object **root ); extern const void *get_req_data_after_objattr( const struct object_attributes *attr, data_size_t *len ); -extern int receive_fd( struct process *process ); -extern int send_client_fd( struct process *process, int fd, obj_handle_t handle ); +extern int receive_fd( struct process *process, enum fd_type type ); +extern int send_client_fd( struct process *process, enum fd_type type, int fd, obj_handle_t handle ); extern void read_request( struct thread *thread ); extern void write_reply( struct thread *thread ); extern timeout_t monotonic_counter(void); diff --git a/server/thread.c b/server/thread.c index 93b77f6b36e..48c0cf16090 100644 --- a/server/thread.c +++ b/server/thread.c @@ -391,9 +391,20 @@ static void apply_thread_priority( struct thread *thread, int effective_priority
#endif
+static void close_fd_type( enum fd_type type, int fd ) +{ + switch (type) + { + case FD_TYPE_NONE: break; + case FD_TYPE_UNIX: close( fd ); break; + default: assert( 0 ); break; + } +} + static void clear_inflight_fd( struct inflight_fd *fd, int need_close ) { - if (need_close) close( fd->server ); + if (need_close) close_fd_type( fd->type, fd->server ); + fd->type = FD_TYPE_NONE; fd->client = fd->server = -1; }
@@ -515,7 +526,7 @@ struct thread *create_thread( int fd, struct process *process, const struct secu file_set_error(); return NULL; } - if (send_client_fd( process, request_pipe[1], SERVER_PROTOCOL_VERSION ) == -1) + if (send_client_fd( process, FD_TYPE_UNIX, request_pipe[1], SERVER_PROTOCOL_VERSION ) == -1) { close( request_pipe[0] ); close( request_pipe[1] ); @@ -1504,20 +1515,20 @@ static void clear_apc_queue( struct list *queue ) }
/* add an fd to the inflight list */ -void thread_add_inflight_fd( struct thread *thread, int client, int server ) +void thread_add_inflight_fd( struct thread *thread, enum fd_type type, int client, int server ) { - struct inflight_fd *fd, new = { .client = client, .server = server }; + struct inflight_fd *fd, new = { .type = type, .client = client, .server = server };
if (client == -1) clear_inflight_fd( &new, 1 ); - if (server == -1) return; + if (server == -1 || type == FD_TYPE_NONE) return;
/* first check if we already have an entry for this fd */ for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) - if (fd->client == new.client) goto done; + if (fd->type == new.type && fd->client == new.client) goto done;
/* now find a free spot to store it */ for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) - if (fd->client == -1) goto done; + if (fd->type == FD_TYPE_NONE) goto done;
fatal_error( "%04x: inflight fd array is full\n", thread->id );
@@ -1528,24 +1539,25 @@ done:
/* get an inflight fd and purge it from the list */ /* the fd must be closed when no longer used */ -int thread_get_inflight_fd( struct thread *thread, int client ) +int thread_get_inflight_fd( struct thread *thread, enum fd_type type, int client ) { struct inflight_fd *fd;
+ if (type == FD_TYPE_NONE) return 0; if (client == -1) return -1;
do { for (fd = thread->inflight; fd < thread->inflight + MAX_INFLIGHT_FDS; fd++) { - if (fd->client == client) + if (fd->type == type && fd->client == client) { int ret = fd->server; clear_inflight_fd( fd, 0 ); return ret; } } - } while (!receive_fd( thread->process )); /* in case it is still in the socket buffer */ + } while (!receive_fd( thread->process, type )); /* in case it is still in the socket buffer */ return -1; }
@@ -1606,7 +1618,7 @@ DECL_HANDLER(new_thread) struct unicode_str name; const struct security_descriptor *sd; const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL ); - int request_fd = thread_get_inflight_fd( current, req->request_fd ); + int request_fd = thread_get_inflight_fd( current, FD_TYPE_UNIX, req->request_fd );
if (!(process = get_process_from_handle( req->process, 0 ))) { @@ -1662,12 +1674,12 @@ done:
static int init_thread( struct thread *thread, int reply_fd, int wait_fd ) { - if ((reply_fd = thread_get_inflight_fd( thread, reply_fd )) == -1) + if ((reply_fd = thread_get_inflight_fd( thread, FD_TYPE_UNIX, reply_fd )) == -1) { set_error( STATUS_TOO_MANY_OPENED_FILES ); return 0; } - if ((wait_fd = thread_get_inflight_fd( thread, wait_fd )) == -1) + if ((wait_fd = thread_get_inflight_fd( thread, FD_TYPE_UNIX, wait_fd )) == -1) { set_error( STATUS_TOO_MANY_OPENED_FILES ); goto error; @@ -1722,7 +1734,7 @@ DECL_HANDLER(init_first_thread) if ((fd = get_inproc_device_fd()) >= 0) { reply->inproc_device = get_process_id( process ) | 1; - send_client_fd( process, fd, reply->inproc_device ); + send_client_fd( process, FD_TYPE_UNIX, fd, reply->inproc_device ); } }
diff --git a/server/thread.h b/server/thread.h index cf9bdf02e65..6a22fa85d02 100644 --- a/server/thread.h +++ b/server/thread.h @@ -42,8 +42,9 @@ enum run_state /* descriptor for fds currently in flight from client to server */ struct inflight_fd { - int client; /* fd on the client side (or -1 if entry is free) */ - int server; /* fd on the server side */ + enum fd_type type; /* fd type, FD_TYPE_NONE if entry is free */ + int client; /* fd on the client side */ + int server; /* fd on the server side */ }; #define MAX_INFLIGHT_FDS 16 /* max number of fds in flight per thread */
@@ -123,8 +124,8 @@ extern void kill_thread( struct thread *thread, int violent_death ); extern void wake_up( struct object *obj, int max ); extern int thread_queue_apc( struct process *process, struct thread *thread, struct object *owner, const union apc_call *call_data ); extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type ); -extern void thread_add_inflight_fd( struct thread *thread, int client, int server ); -extern int thread_get_inflight_fd( struct thread *thread, int client ); +extern void thread_add_inflight_fd( struct thread *thread, enum fd_type type, int client, int server ); +extern int thread_get_inflight_fd( struct thread *thread, enum fd_type type, int client ); extern struct token *thread_get_impersonation_token( struct thread *thread ); extern unsigned int set_thread_priority( struct thread *thread, int priority ); extern unsigned int set_thread_base_priority( struct thread *thread, int base_priority );
From: Rémi Bernon rbernon@codeweavers.com
--- server/protocol.def | 5 +++-- server/request.c | 3 +++ server/thread.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index 8966deeb78e..2dca787ea3a 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -57,8 +57,9 @@ struct request_header
struct reply_header { - unsigned int error; /* error result */ - data_size_t reply_size; /* reply variable part size */ + unsigned int error; /* error result */ + data_size_t reply_size : 30; /* reply variable part size */ + unsigned int fd_type : 2; /* replied inflight fd type if any */ };
enum fd_type diff --git a/server/request.c b/server/request.c index da941c9b59d..86b4338a5ad 100644 --- a/server/request.c +++ b/server/request.c @@ -296,6 +296,7 @@ static void call_req_handler( struct thread *thread )
current = thread; current->reply_size = 0; + current->reply_fd_type = FD_TYPE_NONE; clear_error(); memset( &reply, 0, sizeof(reply) );
@@ -312,6 +313,7 @@ static void call_req_handler( struct thread *thread ) { reply.reply_header.error = current->error; reply.reply_header.reply_size = current->reply_size; + reply.reply_header.fd_type = current->reply_fd_type; if (debug_level) trace_reply( req, &reply ); send_reply( &reply ); } @@ -526,6 +528,7 @@ int send_client_fd( struct process *process, enum fd_type type, int fd, obj_hand default: assert( 0 ); break; }
+ if (!ret && current && process == current->process) current->reply_fd_type = type; return ret; }
diff --git a/server/thread.h b/server/thread.h index 6a22fa85d02..b604b263489 100644 --- a/server/thread.h +++ b/server/thread.h @@ -70,6 +70,7 @@ struct thread unsigned int req_toread; /* amount of data still to read in request */ void *reply_data; /* variable-size data for reply */ unsigned int reply_size; /* size of reply data */ + unsigned int reply_fd_type; /* type of replied fd */ unsigned int reply_towrite; /* amount of data still to write in reply */ struct fd *request_fd; /* fd for receiving client requests */ struct fd *reply_fd; /* fd to send a reply to a client */
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/ntdll/unix/server.c | 45 ++++++++++++++++++++++++++++++++++++++++ include/wine/server.h | 2 ++ 2 files changed, 47 insertions(+)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index e8da94ebc64..56c2bad40ed 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -1495,6 +1495,51 @@ static void send_server_task_port(void) #endif /* __APPLE__ */
+/*********************************************************************** + * wine_server_call_send_unix_fd + * + * Send an fd and perform a server call. + */ +unsigned int CDECL wine_server_call_send_fd( void *req_ptr, enum fd_type type, int fd ) +{ + switch (type) + { + case FD_TYPE_NONE: break; + case FD_TYPE_UNIX: wine_server_send_fd( fd ); break; + default: assert( 0 ); break; + } + return wine_server_call( req_ptr ); +} + + +/*********************************************************************** + * wine_server_call_receive_fd + * + * Perform a server call and receive an fd. + */ +unsigned int CDECL wine_server_call_receive_fd( void *req_ptr, enum fd_type *type, int *fd, obj_handle_t *handle ) +{ + struct __server_request_info * const req = req_ptr; + unsigned int ret; + sigset_t sigset; + + server_enter_uninterrupted_section( &fd_cache_mutex, &sigset ); + + if ((ret = server_call_unlocked( req_ptr ))) *handle = *fd = -1; + else switch ((*type = req->u.reply.reply_header.fd_type)) + { + case FD_TYPE_NONE: *fd = *handle = 0; break; + case FD_TYPE_UNIX: *fd = wine_server_receive_fd( handle ); break; + default: assert( 0 ); break; + } + + server_leave_uninterrupted_section( &fd_cache_mutex, &sigset ); + + if (!ret && *fd == -1) ret = STATUS_TOO_MANY_OPENED_FILES; + return ret; +} + + /*********************************************************************** * get_unix_tid * diff --git a/include/wine/server.h b/include/wine/server.h index 458c33c27b5..5e4c58f252f 100644 --- a/include/wine/server.h +++ b/include/wine/server.h @@ -50,6 +50,8 @@ struct __server_request_info };
NTSYSAPI unsigned int CDECL wine_server_call( void *req_ptr ); +NTSYSAPI unsigned int CDECL wine_server_call_send_fd( void *req_ptr, enum fd_type type, int fd ); +NTSYSAPI unsigned int CDECL wine_server_call_receive_fd( void *req_ptr, enum fd_type *type, int *fd, obj_handle_t *handle ); NTSYSAPI NTSTATUS CDECL wine_server_fd_to_handle( int fd, unsigned int access, unsigned int attributes, HANDLE *handle ); NTSYSAPI NTSTATUS CDECL wine_server_handle_to_fd( HANDLE handle, unsigned int access, int *unix_fd, unsigned int *options );
I pushed the mach port support to https://gitlab.winehq.org/rbernon/wine/-/commits/wip/mach-ports for visibility.
There's maybe an option to use `wine_server_fd_to_handle` / `wine_server_handle_to_fd` instead, but that would require creating file objects for every resource on the server side, and that seemed a bit unnecessary.
That seems better than redesigning the entire thing.
Well except that they are not actual files, and I doubt things like `fstat` would work for those (which `alloc_file_handle` calls), in addition to the fact that this will need to deal with mach port too and that this would require additional requests when only one would be needed otherwise.
I don't think that adding complexity to the generic request path just so we can make mach ports look like file descriptors is a good trade-off.
And there's nothing that says that `wine_server_fd_to_handle` needs actual files, it should be able to support any file descriptor.
I don't think that adding complexity to the generic request path just so we can make mach ports look like file descriptors is a good trade-off.
I don't understand what complexity you mean? Is this about the added field in the generic reply? It could probably be moved out of there if it's really a concern.
Wrt mach ports, sure can keep them separate from fds, but they would still require the same mechanisms to receive them, as `thread_get_inflight_fd` on the server side, and as the process-wide mutex on the client side, because like fds we still have only one per-process channel to send them over. So IMO in this case it's better to reuse what is there than duplicate it.
And there's nothing that says that `wine_server_fd_to_handle` needs actual files, it should be able to support any file descriptor.
Well maybe, but we wouldn't need the server file object at all. It seems silly to me to have to go through it and extra requests just to send an fd. Fwiw I added a helper for symmetry with the `wine_server_receive_fd` one, but all we would actually need for sending is to export and call `wine_server_send_fd` (and eventually `wine_server_send_mach_port`) from win32u.
If we can expose `wine_server_send_fd` (and later similar for mach_port) to win32u, then I can probably live with `wine_server_handle_to_fd` instead on the receiving side, (and a new wine_server_handle_to_mach_port). It's still an extra request when we should IMO have a more flexible way but it's maybe ok for now.
This merge request was closed by Rémi Bernon.