diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a254128a4b5..2c486e2326a 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2395,6 +2395,18 @@ struct get_msg_queue_reply +struct set_queue_fd_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct set_queue_fd_reply +{ + struct reply_header __header; +}; + + + struct set_queue_mask_request { struct request_header __header; @@ -4118,6 +4130,7 @@ enum request REQ_empty_atom_table, REQ_init_atom_table, REQ_get_msg_queue, + REQ_set_queue_fd, REQ_set_queue_mask, REQ_get_queue_status, REQ_get_process_idle_event, @@ -4341,6 +4354,7 @@ union generic_request struct empty_atom_table_request empty_atom_table_request; struct init_atom_table_request init_atom_table_request; struct get_msg_queue_request get_msg_queue_request; + struct set_queue_fd_request set_queue_fd_request; struct set_queue_mask_request set_queue_mask_request; struct get_queue_status_request get_queue_status_request; struct get_process_idle_event_request get_process_idle_event_request; @@ -4562,6 +4576,7 @@ union generic_reply struct empty_atom_table_reply empty_atom_table_reply; struct init_atom_table_reply init_atom_table_reply; struct get_msg_queue_reply get_msg_queue_reply; + struct set_queue_fd_reply set_queue_fd_reply; struct set_queue_mask_reply set_queue_mask_reply; struct get_queue_status_reply get_queue_status_reply; struct get_process_idle_event_reply get_process_idle_event_reply; @@ -4662,6 +4677,6 @@ union generic_reply struct allocate_locally_unique_id_reply allocate_locally_unique_id_reply; }; -#define SERVER_PROTOCOL_VERSION 289 +#define SERVER_PROTOCOL_VERSION 290 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index ef202350249..48d1096589c 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1783,6 +1783,12 @@ enum char_info_mode @END +/* Set the file descriptor associated to the current thread queue */ +@REQ(set_queue_fd) + obj_handle_t handle; /* handle to the file descriptor */ +@END + + /* Set the current message queue wakeup mask */ @REQ(set_queue_mask) unsigned int wake_mask; /* wakeup bits mask */ diff --git a/server/queue.c b/server/queue.c index 197315270d2..4e66f990e2b 100644 --- a/server/queue.c +++ b/server/queue.c @@ -113,6 +113,7 @@ struct thread_input struct msg_queue { struct object obj; /* object header */ + struct fd *fd; /* optional file descriptor to poll */ unsigned int wake_bits; /* wakeup bits */ unsigned int wake_mask; /* wakeup mask */ unsigned int changed_bits; /* changed wakeup bits */ @@ -139,6 +140,7 @@ static void msg_queue_remove_queue( struct object *obj, struct wait_queue_entry static int msg_queue_signaled( struct object *obj, struct thread *thread ); static int msg_queue_satisfied( struct object *obj, struct thread *thread ); static void msg_queue_destroy( struct object *obj ); +static void msg_queue_poll_event( struct fd *fd, int event ); static void thread_input_dump( struct object *obj, int verbose ); static void thread_input_destroy( struct object *obj ); static void timer_callback( void *private ); @@ -160,6 +162,16 @@ static const struct object_ops msg_queue_ops = msg_queue_destroy /* destroy */ }; +static const struct fd_ops msg_queue_fd_ops = +{ + NULL, /* get_poll_events */ + msg_queue_poll_event, /* poll_event */ + no_flush, /* flush */ + no_get_file_info, /* get_file_info */ + no_queue_async, /* queue_async */ + no_cancel_async /* cancel async */ +}; + static const struct object_ops thread_input_ops = { @@ -243,6 +255,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ if (!input && !(input = create_thread_input( thread ))) return NULL; if ((queue = alloc_object( &msg_queue_ops ))) { + queue->fd = NULL; queue->wake_bits = 0; queue->wake_mask = 0; queue->changed_bits = 0; @@ -778,6 +791,8 @@ static int msg_queue_add_queue( struct object *obj, struct wait_queue_entry *ent { if (process->idle_event) set_event( process->idle_event ); } + if (queue->fd && list_empty( &obj->wait_queue )) /* first on the queue */ + set_fd_events( queue->fd, POLLIN ); add_queue( obj, entry ); return 1; } @@ -788,6 +803,8 @@ static void msg_queue_remove_queue(struct object *obj, struct wait_queue_entry * struct process *process = entry->thread->process; remove_queue( obj, entry ); + if (queue->fd && list_empty( &obj->wait_queue )) /* last on the queue is gone */ + set_fd_events( queue->fd, 0 ); assert( entry->thread->queue == queue ); @@ -808,7 +825,18 @@ static void msg_queue_dump( struct object *obj, int verbose ) static int msg_queue_signaled( struct object *obj, struct thread *thread ) { struct msg_queue *queue = (struct msg_queue *)obj; - return is_signaled( queue ); + int ret = 0; + + if (queue->fd) + { + if ((ret = check_fd_events( queue->fd, POLLIN ))) + /* stop waiting on select() if we are signaled */ + set_fd_events( queue->fd, 0 ); + else if (!list_empty( &obj->wait_queue )) + /* restart waiting on poll() if we are no longer signaled */ + set_fd_events( queue->fd, POLLIN ); + } + return ret || is_signaled( queue ); } static int msg_queue_satisfied( struct object *obj, struct thread *thread ) @@ -843,6 +871,16 @@ static void msg_queue_destroy( struct object *obj ) if (queue->timeout) remove_timeout_user( queue->timeout ); if (queue->input) release_object( queue->input ); if (queue->hooks) release_object( queue->hooks ); + if (queue->fd) release_object( queue->fd ); +} + +static void msg_queue_poll_event( struct fd *fd, int event ) +{ + struct msg_queue *queue = get_fd_user( fd ); + assert( queue->obj.ops == &msg_queue_ops ); + + if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); + wake_up( &queue->obj, 0 ); } static void thread_input_dump( struct object *obj, int verbose ) @@ -1524,6 +1562,31 @@ DECL_HANDLER(get_msg_queue) } +/* set the file descriptor associated to the current thread queue */ +DECL_HANDLER(set_queue_fd) +{ + struct msg_queue *queue = get_current_queue(); + struct file *file; + int unix_fd; + + if (queue->fd) /* fd can only be set once */ + { + set_error( STATUS_ACCESS_DENIED ); + return; + } + if (!(file = get_file_obj( current->process, req->handle, SYNCHRONIZE ))) return; + + if ((unix_fd = get_file_unix_fd( file )) != -1) + { + if ((unix_fd = dup( unix_fd )) != -1) + queue->fd = create_anonymous_fd( &msg_queue_fd_ops, unix_fd, &queue->obj ); + else + file_set_error(); + } + release_object( file ); +} + + /* set the current message queue wakeup mask */ DECL_HANDLER(set_queue_mask) { diff --git a/server/request.h b/server/request.h index aa4f8e4b7f5..5b28a7f0772 100644 --- a/server/request.h +++ b/server/request.h @@ -228,6 +228,7 @@ DECL_HANDLER(set_atom_information); DECL_HANDLER(empty_atom_table); DECL_HANDLER(init_atom_table); DECL_HANDLER(get_msg_queue); +DECL_HANDLER(set_queue_fd); DECL_HANDLER(set_queue_mask); DECL_HANDLER(get_queue_status); DECL_HANDLER(get_process_idle_event); @@ -450,6 +451,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_empty_atom_table, (req_handler)req_init_atom_table, (req_handler)req_get_msg_queue, + (req_handler)req_set_queue_fd, (req_handler)req_set_queue_mask, (req_handler)req_get_queue_status, (req_handler)req_get_process_idle_event, diff --git a/server/trace.c b/server/trace.c index c4572bd9efd..0d11e7cf88d 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2214,6 +2214,11 @@ static void dump_get_msg_queue_reply( const struct get_msg_queue_reply *req ) fprintf( stderr, " handle=%p", req->handle ); } +static void dump_set_queue_fd_request( const struct set_queue_fd_request *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + static void dump_set_queue_mask_request( const struct set_queue_mask_request *req ) { fprintf( stderr, " wake_mask=%08x,", req->wake_mask ); @@ -3568,6 +3573,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_empty_atom_table_request, (dump_func)dump_init_atom_table_request, (dump_func)dump_get_msg_queue_request, + (dump_func)dump_set_queue_fd_request, (dump_func)dump_set_queue_mask_request, (dump_func)dump_get_queue_status_request, (dump_func)dump_get_process_idle_event_request, @@ -3787,6 +3793,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)dump_init_atom_table_reply, (dump_func)dump_get_msg_queue_reply, + (dump_func)0, (dump_func)dump_set_queue_mask_reply, (dump_func)dump_get_queue_status_reply, (dump_func)dump_get_process_idle_event_reply, @@ -4006,6 +4013,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "empty_atom_table", "init_atom_table", "get_msg_queue", + "set_queue_fd", "set_queue_mask", "get_queue_status", "get_process_idle_event",