server: Add a generic apc_call structure to make it easier to extend, and more type-safe.

This commit is contained in:
Alexandre Julliard 2007-01-04 13:40:09 +01:00
parent 49b49c30fc
commit 5c8421d3e7
11 changed files with 190 additions and 95 deletions

View File

@ -660,14 +660,11 @@ static int wait_reply( void *cookie )
static void call_apcs( BOOL alertable )
{
NTSTATUS ret;
apc_call_t call;
HANDLE handle = 0;
FARPROC proc;
LARGE_INTEGER time;
void *arg1, *arg2, *arg3;
for (;;)
{
int type = APC_NONE;
SERVER_START_REQ( get_apc )
{
req->alertable = alertable;
@ -675,34 +672,33 @@ static void call_apcs( BOOL alertable )
if (!(ret = wine_server_call( req )))
{
handle = reply->handle;
type = reply->type;
proc = reply->func;
arg1 = reply->arg1;
arg2 = reply->arg2;
arg3 = reply->arg3;
call = reply->call;
}
}
SERVER_END_REQ;
if (ret) return; /* no more APCs */
switch (type)
switch (call.type)
{
case APC_USER:
proc( arg1, arg2, arg3 );
call.user.func( call.user.args[0], call.user.args[1], call.user.args[2] );
break;
case APC_TIMER:
{
LARGE_INTEGER time;
/* convert sec/usec to NT time */
RtlSecondsSince1970ToTime( (time_t)arg1, &time );
time.QuadPart += (DWORD)arg2 * 10;
proc( arg3, time.u.LowPart, time.u.HighPart );
RtlSecondsSince1970ToTime( call.timer.time.sec, &time );
time.QuadPart += call.timer.time.usec * 10;
call.timer.func( call.timer.arg, time.u.LowPart, time.u.HighPart );
break;
}
case APC_ASYNC_IO:
NtCurrentTeb()->num_async_io--;
proc( arg1, (IO_STATUS_BLOCK*)arg2, (ULONG)arg3 );
call.async_io.func( call.async_io.user, call.async_io.sb, call.async_io.status );
break;
default:
server_protocol_error( "get_apc_request: bad type %d\n", type );
server_protocol_error( "get_apc_request: bad type %d\n", call.type );
break;
}
}

View File

@ -638,11 +638,15 @@ NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1
SERVER_START_REQ( queue_apc )
{
req->handle = handle;
req->user = 1;
req->func = func;
req->arg1 = (void *)arg1;
req->arg2 = (void *)arg2;
req->arg3 = (void *)arg3;
if (func)
{
req->call.type = APC_USER;
req->call.user.func = func;
req->call.user.args[0] = arg1;
req->call.user.args[1] = arg2;
req->call.user.args[2] = arg3;
}
else req->call.type = APC_NONE; /* wake up only */
ret = wine_server_call( req );
}
SERVER_END_REQ;

View File

@ -209,6 +209,34 @@ struct token_groups
};
enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
typedef union
{
enum apc_type type;
struct
{
enum apc_type type;
void (__stdcall *func)(unsigned long,unsigned long,unsigned long);
unsigned long args[3];
} user;
struct
{
enum apc_type type;
void (__stdcall *func)(void*, unsigned int, unsigned int);
abs_time_t time;
void *arg;
} timer;
struct
{
enum apc_type type;
void (__stdcall *func)(void*, void*, unsigned int);
void *user;
void *sb;
unsigned int status;
} async_io;
} apc_call_t;
@ -512,11 +540,7 @@ struct queue_apc_request
{
struct request_header __header;
obj_handle_t handle;
int user;
void* func;
void* arg1;
void* arg2;
void* arg3;
apc_call_t call;
};
struct queue_apc_reply
{
@ -535,13 +559,8 @@ struct get_apc_reply
{
struct reply_header __header;
obj_handle_t handle;
void* func;
int type;
void* arg1;
void* arg2;
void* arg3;
apc_call_t call;
};
enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
@ -4417,6 +4436,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply;
};
#define SERVER_PROTOCOL_VERSION 262
#define SERVER_PROTOCOL_VERSION 263
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */

View File

@ -1283,8 +1283,15 @@ struct async
/* destroys the server side of it */
static void async_terminate( struct async *async, int status )
{
thread_queue_apc( async->thread, NULL, async->apc, APC_ASYNC_IO,
1, async->user, async->sb, (void *)status );
apc_call_t data;
memset( &data, 0, sizeof(data) );
data.type = APC_ASYNC_IO;
data.async_io.func = async->apc;
data.async_io.user = async->user;
data.async_io.sb = async->sb;
data.async_io.status = status;
thread_queue_apc( async->thread, NULL, &data );
if (async->timeout) remove_timeout_user( async->timeout );
async->timeout = NULL;

View File

@ -857,9 +857,16 @@ DECL_HANDLER(wait_named_pipe)
server = find_server( pipe, ps_wait_open );
if (server)
{
apc_call_t data;
/* there's already a server waiting for a client to connect */
thread_queue_apc( current, NULL, req->func, APC_ASYNC_IO,
1, req->event, NULL, (void *)STATUS_SUCCESS );
memset( &data, 0, sizeof(data) );
data.type = APC_ASYNC_IO;
data.async_io.func = req->func;
data.async_io.user = req->event;
data.async_io.sb = NULL;
data.async_io.status = STATUS_SUCCESS;
thread_queue_apc( current, NULL, &data );
release_object( server );
}
else

View File

@ -225,6 +225,34 @@ struct token_groups
/* VARARGS(sids,SID); */
};
enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
typedef union
{
enum apc_type type;
struct
{
enum apc_type type; /* APC_USER */
void (__stdcall *func)(unsigned long,unsigned long,unsigned long);
unsigned long args[3]; /* arguments for user function */
} user;
struct
{
enum apc_type type; /* APC_TIMER */
void (__stdcall *func)(void*, unsigned int, unsigned int);
abs_time_t time; /* absolute time of expiration */
void *arg; /* user argument */
} timer;
struct
{
enum apc_type type; /* APC_ASYNC_IO */
void (__stdcall *func)(void*, void*, unsigned int);
void *user; /* user pointer */
void *sb; /* status block */
unsigned int status; /* I/O status */
} async_io;
} apc_call_t;
/****************************************************************/
/* Request declarations */
@ -436,11 +464,7 @@ struct token_groups
/* Queue an APC for a thread */
@REQ(queue_apc)
obj_handle_t handle; /* thread handle */
int user; /* user or system apc? */
void* func; /* function to call */
void* arg1; /* params for function to call */
void* arg2;
void* arg3;
apc_call_t call; /* call arguments */
@END
@ -450,13 +474,8 @@ struct token_groups
obj_handle_t prev; /* handle to previous APC */
@REPLY
obj_handle_t handle; /* handle to APC */
void* func; /* function to call */
int type; /* function type */
void* arg1; /* function arguments */
void* arg2;
void* arg3;
apc_call_t call; /* call arguments */
@END
enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO };
/* Close a handle for the current process */

View File

@ -72,11 +72,7 @@ struct thread_apc
struct list entry; /* queue linked list */
struct object *owner; /* object that queued this apc */
int executed; /* has it been executed by the client? */
void *func; /* function to call in client */
enum apc_type type; /* type of apc function */
void *arg1; /* function arguments */
void *arg2;
void *arg3;
apc_call_t call;
};
static void dump_thread_apc( struct object *obj, int verbose );
@ -306,7 +302,7 @@ static void dump_thread_apc( struct object *obj, int verbose )
struct thread_apc *apc = (struct thread_apc *)obj;
assert( obj->ops == &thread_apc_ops );
fprintf( stderr, "APC owner=%p type=%u\n", apc->owner, apc->type );
fprintf( stderr, "APC owner=%p type=%u\n", apc->owner, apc->call.type );
}
static int thread_apc_signaled( struct object *obj, struct thread *thread )
@ -663,24 +659,33 @@ void wake_up( struct object *obj, int max )
}
}
/* return the apc queue to use for a given apc type */
static inline struct list *get_apc_queue( struct thread *thread, enum apc_type type )
{
switch(type)
{
case APC_NONE:
case APC_USER:
case APC_TIMER:
return &thread->user_apc;
default:
return &thread->system_apc;
}
}
/* queue an async procedure call */
int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
enum apc_type type, int system, void *arg1, void *arg2, void *arg3 )
int thread_queue_apc( struct thread *thread, struct object *owner, const apc_call_t *call_data )
{
struct thread_apc *apc;
struct list *queue = system ? &thread->system_apc : &thread->user_apc;
struct list *queue = get_apc_queue( thread, call_data->type );
/* cancel a possible previous APC with the same owner */
if (owner) thread_cancel_apc( thread, owner, system );
if (owner) thread_cancel_apc( thread, owner, call_data->type );
if (thread->state == TERMINATED) return 0;
if (!(apc = alloc_object( &thread_apc_ops ))) return 0;
apc->owner = owner;
apc->func = func;
apc->type = type;
apc->arg1 = arg1;
apc->arg2 = arg2;
apc->arg3 = arg3;
apc->call = *call_data;
apc->owner = owner;
apc->executed = 0;
list_add_tail( queue, &apc->entry );
if (!list_prev( queue, &apc->entry )) /* first one */
@ -690,10 +695,11 @@ int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
}
/* cancel the async procedure call owned by a specific object */
void thread_cancel_apc( struct thread *thread, struct object *owner, int system )
void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type )
{
struct thread_apc *apc;
struct list *queue = system ? &thread->system_apc : &thread->user_apc;
struct list *queue = get_apc_queue( thread, type );
LIST_FOR_EACH_ENTRY( apc, queue, struct thread_apc, entry )
{
if (apc->owner != owner) continue;
@ -1053,8 +1059,16 @@ DECL_HANDLER(queue_apc)
struct thread *thread;
if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
{
thread_queue_apc( thread, NULL, req->func, APC_USER, !req->user,
req->arg1, req->arg2, req->arg3 );
switch( req->call.type )
{
case APC_NONE:
case APC_USER:
thread_queue_apc( thread, NULL, &req->call );
break;
default:
set_error( STATUS_INVALID_PARAMETER );
break;
}
release_object( thread );
}
}
@ -1082,22 +1096,15 @@ DECL_HANDLER(get_apc)
set_error( STATUS_PENDING );
return;
}
/* Optimization: ignore APCs that have a NULL func; they are only used
* to wake up a thread, but since we got here the thread woke up already.
* Exception: for APC_ASYNC_IO, func == NULL is legal.
/* Optimization: ignore APC_NONE calls, they are only used to
* wake up a thread, but since we got here the thread woke up already.
*/
if (apc->func || apc->type == APC_ASYNC_IO) break;
if (apc->call.type != APC_NONE) break;
release_object( apc );
}
if ((reply->handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 )))
{
reply->func = apc->func;
reply->type = apc->type;
reply->arg1 = apc->arg1;
reply->arg2 = apc->arg2;
reply->arg3 = apc->arg3;
}
reply->call = apc->call;
release_object( apc );
}

View File

@ -112,9 +112,8 @@ extern void remove_queue( struct object *obj, struct wait_queue_entry *entry );
extern void kill_thread( struct thread *thread, int violent_death );
extern void break_thread( struct thread *thread );
extern void wake_up( struct object *obj, int max );
extern int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
enum apc_type type, int system, void *arg1, void *arg2, void *arg3 );
extern void thread_cancel_apc( struct thread *thread, struct object *owner, int system );
extern int thread_queue_apc( struct thread *thread, struct object *owner, const apc_call_t *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 int thread_get_inflight_fd( struct thread *thread, int client );
extern struct thread_snapshot *thread_snap( int *count );

View File

@ -104,8 +104,20 @@ static void timer_callback( void *private )
/* queue an APC */
if (timer->thread)
{
if (!thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 0,
(void *)timer->when.tv_sec, (void *)timer->when.tv_usec, timer->arg))
apc_call_t data;
memset( &data, 0, sizeof(data) );
if (timer->callback)
{
data.type = APC_TIMER;
data.timer.func = timer->callback;
data.timer.time.sec = timer->when.tv_sec;
data.timer.time.usec = timer->when.tv_usec;
data.timer.arg = timer->arg;
}
else data.type = APC_NONE; /* wake up only */
if (!thread_queue_apc( timer->thread, &timer->obj, &data ))
{
release_object( timer->thread );
timer->thread = NULL;
@ -136,7 +148,7 @@ static int cancel_timer( struct timer *timer )
}
if (timer->thread)
{
thread_cancel_apc( timer->thread, &timer->obj, 0 );
thread_cancel_apc( timer->thread, &timer->obj, APC_TIMER );
release_object( timer->thread );
timer->thread = NULL;
}

View File

@ -43,6 +43,8 @@
static const void *cur_data;
static data_size_t cur_size;
static const char *get_status_name( unsigned int status );
/* utility functions */
inline static void remove_data( data_size_t size )
@ -96,6 +98,34 @@ static void dump_char_info( const char_info_t *info )
fprintf( stderr, "',%04x}", info->attr );
}
static void dump_apc_call( const apc_call_t *call )
{
fputc( '{', stderr );
switch(call->type)
{
case APC_NONE:
fprintf( stderr, "APC_NONE" );
break;
case APC_USER:
fprintf( stderr, "APC_USER,args={%lx,%lx,%lx}",
call->user.args[0], call->user.args[1], call->user.args[2] );
break;
case APC_TIMER:
fprintf( stderr, "APC_TIMER,time=" );
dump_abs_time( &call->timer.time );
fprintf( stderr, ",arg=%p", call->timer.arg );
break;
case APC_ASYNC_IO:
fprintf( stderr, "APC_ASYNC_IO,user=%p,sb=%p,status=%s",
call->async_io.user, call->async_io.sb, get_status_name(call->async_io.status) );
break;
default:
fprintf( stderr, "type=%u", call->type );
break;
}
fputc( '}', stderr );
}
static void dump_context( const CONTEXT *context )
{
#ifdef __i386__
@ -845,11 +875,8 @@ static void dump_unload_dll_request( const struct unload_dll_request *req )
static void dump_queue_apc_request( const struct queue_apc_request *req )
{
fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " user=%d,", req->user );
fprintf( stderr, " func=%p,", req->func );
fprintf( stderr, " arg1=%p,", req->arg1 );
fprintf( stderr, " arg2=%p,", req->arg2 );
fprintf( stderr, " arg3=%p", req->arg3 );
fprintf( stderr, " call=" );
dump_apc_call( &req->call );
}
static void dump_get_apc_request( const struct get_apc_request *req )
@ -861,11 +888,8 @@ static void dump_get_apc_request( const struct get_apc_request *req )
static void dump_get_apc_reply( const struct get_apc_reply *req )
{
fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " func=%p,", req->func );
fprintf( stderr, " type=%d,", req->type );
fprintf( stderr, " arg1=%p,", req->arg1 );
fprintf( stderr, " arg2=%p,", req->arg2 );
fprintf( stderr, " arg3=%p", req->arg3 );
fprintf( stderr, " call=" );
dump_apc_call( &req->call );
}
static void dump_close_handle_request( const struct close_handle_request *req )

View File

@ -42,6 +42,7 @@ my %formats =
"abs_time_t" => "&dump_abs_time",
"rectangle_t" => "&dump_rectangle",
"char_info_t" => "&dump_char_info",
"apc_call_t" => "&dump_apc_call",
);
my @requests = ();