wine/server/thread.c
Alexandre Julliard 0562539d18 Implemented file sharing checks in the server.
Added set file time server request.
Overall clean up of the file handling (DOS device handling is now
broken, should be redone).
1999-01-03 11:55:56 +00:00

418 lines
11 KiB
C

/*
* Server-side thread management
*
* Copyright (C) 1998 Alexandre Julliard
*/
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "winnt.h"
#include "winerror.h"
#include "server.h"
#include "server/thread.h"
/* thread queues */
struct wait_queue_entry
{
struct wait_queue_entry *next;
struct wait_queue_entry *prev;
struct object *obj;
struct thread *thread;
};
struct thread_wait
{
int count; /* count of objects */
int flags;
struct timeval timeout;
struct wait_queue_entry queues[1];
};
/* thread operations */
static void dump_thread( struct object *obj, int verbose );
static int thread_signaled( struct object *obj, struct thread *thread );
static void destroy_thread( struct object *obj );
static const struct object_ops thread_ops =
{
dump_thread,
add_queue,
remove_queue,
thread_signaled,
no_satisfied,
no_read_fd,
no_write_fd,
no_flush,
no_get_file_info,
destroy_thread
};
static struct thread *first_thread;
/* create a new thread */
struct thread *create_thread( int fd, void *pid, int *thread_handle,
int *process_handle )
{
struct thread *thread;
struct process *process;
if (!(thread = mem_alloc( sizeof(*thread) ))) return NULL;
if (pid) process = get_process_from_id( pid );
else process = create_process();
if (!process)
{
free( thread );
return NULL;
}
init_object( &thread->obj, &thread_ops, NULL );
thread->client_fd = fd;
thread->process = process;
thread->unix_pid = 0; /* not known yet */
thread->name = NULL;
thread->mutex = NULL;
thread->wait = NULL;
thread->error = 0;
thread->state = STARTING;
thread->exit_code = 0x103; /* STILL_ACTIVE */
thread->next = first_thread;
thread->prev = NULL;
if (first_thread) first_thread->prev = thread;
first_thread = thread;
add_process_thread( process, thread );
*thread_handle = *process_handle = -1;
if (current)
{
if ((*thread_handle = alloc_handle( current->process, thread,
THREAD_ALL_ACCESS, 0 )) == -1)
goto error;
}
if (current && !pid)
{
if ((*process_handle = alloc_handle( current->process, process,
PROCESS_ALL_ACCESS, 0 )) == -1)
goto error;
}
if (add_client( fd, thread ) == -1) goto error;
return thread;
error:
if (current)
{
close_handle( current->process, *thread_handle );
close_handle( current->process, *process_handle );
}
remove_process_thread( process, thread );
release_object( thread );
return NULL;
}
/* destroy a thread when its refcount is 0 */
static void destroy_thread( struct object *obj )
{
struct thread *thread = (struct thread *)obj;
assert( obj->ops == &thread_ops );
release_object( thread->process );
if (thread->next) thread->next->prev = thread->prev;
if (thread->prev) thread->prev->next = thread->next;
else first_thread = thread->next;
if (thread->name) free( thread->name );
if (debug_level) memset( thread, 0xaa, sizeof(thread) ); /* catch errors */
free( thread );
}
/* dump a thread on stdout for debugging purposes */
static void dump_thread( struct object *obj, int verbose )
{
struct thread *thread = (struct thread *)obj;
assert( obj->ops == &thread_ops );
fprintf( stderr, "Thread pid=%d fd=%d name='%s'\n",
thread->unix_pid, thread->client_fd, thread->name );
}
static int thread_signaled( struct object *obj, struct thread *thread )
{
struct thread *mythread = (struct thread *)obj;
return (mythread->state == TERMINATED);
}
/* get a thread pointer from a thread id (and increment the refcount) */
struct thread *get_thread_from_id( void *id )
{
struct thread *t = first_thread;
while (t && (t != id)) t = t->next;
if (t) grab_object( t );
return t;
}
/* get a thread from a handle (and increment the refcount) */
struct thread *get_thread_from_handle( int handle, unsigned int access )
{
return (struct thread *)get_handle_obj( current->process, handle,
access, &thread_ops );
}
/* get all information about a thread */
void get_thread_info( struct thread *thread,
struct get_thread_info_reply *reply )
{
reply->pid = thread;
reply->exit_code = thread->exit_code;
}
/* send a reply to a thread */
int send_reply( struct thread *thread, int pass_fd, int n,
... /* arg_1, len_1, ..., arg_n, len_n */ )
{
struct iovec vec[16];
va_list args;
int i;
assert( n < 16 );
va_start( args, n );
for (i = 0; i < n; i++)
{
vec[i].iov_base = va_arg( args, void * );
vec[i].iov_len = va_arg( args, int );
}
va_end( args );
return send_reply_v( thread->client_fd, thread->error, pass_fd, vec, n );
}
/* add a thread to an object wait queue; return 1 if OK, 0 on error */
int add_queue( struct object *obj, struct wait_queue_entry *entry )
{
grab_object( obj );
entry->obj = obj;
entry->prev = obj->tail;
entry->next = NULL;
if (obj->tail) obj->tail->next = entry;
else obj->head = entry;
obj->tail = entry;
return 1;
}
/* remove a thread from an object wait queue */
void remove_queue( struct object *obj, struct wait_queue_entry *entry )
{
if (entry->next) entry->next->prev = entry->prev;
else obj->tail = entry->prev;
if (entry->prev) entry->prev->next = entry->next;
else obj->head = entry->next;
release_object( obj );
}
/* finish waiting */
static void end_wait( struct thread *thread )
{
struct thread_wait *wait = thread->wait;
struct wait_queue_entry *entry;
int i;
assert( wait );
for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
entry->obj->ops->remove_queue( entry->obj, entry );
if (wait->flags & SELECT_TIMEOUT) set_select_timeout( thread->client_fd, NULL );
free( wait );
thread->wait = NULL;
}
/* build the thread wait structure */
static int wait_on( struct thread *thread, int count,
int *handles, int flags, int timeout )
{
struct thread_wait *wait;
struct wait_queue_entry *entry;
struct object *obj;
int i;
if ((count < 0) || (count > MAXIMUM_WAIT_OBJECTS))
{
SET_ERROR( ERROR_INVALID_PARAMETER );
return 0;
}
if (!(wait = mem_alloc( sizeof(*wait) + (count-1) * sizeof(*entry) ))) return 0;
thread->wait = wait;
wait->count = count;
wait->flags = flags;
if (flags & SELECT_TIMEOUT)
{
gettimeofday( &wait->timeout, 0 );
if (timeout)
{
wait->timeout.tv_usec += (timeout % 1000) * 1000;
if (wait->timeout.tv_usec >= 1000000)
{
wait->timeout.tv_usec -= 1000000;
wait->timeout.tv_sec++;
}
wait->timeout.tv_sec += timeout / 1000;
}
}
for (i = 0, entry = wait->queues; i < count; i++, entry++)
{
if (!(obj = get_handle_obj( thread->process, handles[i],
SYNCHRONIZE, NULL )))
{
wait->count = i - 1;
end_wait( thread );
return 0;
}
entry->thread = thread;
if (!obj->ops->add_queue( obj, entry ))
{
wait->count = i - 1;
end_wait( thread );
return 0;
}
release_object( obj );
}
return 1;
}
/* check if the thread waiting condition is satisfied */
static int check_wait( struct thread *thread, int *signaled )
{
int i;
struct thread_wait *wait = thread->wait;
struct wait_queue_entry *entry = wait->queues;
struct timeval now;
assert( wait );
if (wait->flags & SELECT_ALL)
{
for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
if (!entry->obj->ops->signaled( entry->obj, thread )) goto check_timeout;
/* Wait satisfied: tell it to all objects */
*signaled = 0;
for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
if (entry->obj->ops->satisfied( entry->obj, thread ))
*signaled = STATUS_ABANDONED_WAIT_0;
return 1;
}
else
{
for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
{
if (!entry->obj->ops->signaled( entry->obj, thread )) continue;
/* Wait satisfied: tell it to the object */
*signaled = i;
if (entry->obj->ops->satisfied( entry->obj, thread ))
*signaled += STATUS_ABANDONED_WAIT_0;
return 1;
}
}
check_timeout:
if (!(wait->flags & SELECT_TIMEOUT)) return 0;
gettimeofday( &now, NULL );
if ((now.tv_sec > wait->timeout.tv_sec) ||
((now.tv_sec == wait->timeout.tv_sec) &&
(now.tv_usec >= wait->timeout.tv_usec)))
{
*signaled = STATUS_TIMEOUT;
return 1;
}
return 0;
}
/* sleep on a list of objects */
void sleep_on( struct thread *thread, int count, int *handles, int flags, int timeout )
{
struct select_reply reply;
assert( !thread->wait );
reply.signaled = -1;
if (!wait_on( thread, count, handles, flags, timeout )) goto done;
if (!check_wait( thread, &reply.signaled ))
{
/* we need to wait */
if (flags & SELECT_TIMEOUT)
set_select_timeout( thread->client_fd, &thread->wait->timeout );
return;
}
end_wait( thread );
done:
send_reply( thread, -1, 1, &reply, sizeof(reply) );
}
/* attempt to wake up a thread */
/* return 1 if OK, 0 if the wait condition is still not satisfied */
static int wake_thread( struct thread *thread )
{
struct select_reply reply;
if (!check_wait( thread, &reply.signaled )) return 0;
end_wait( thread );
send_reply( thread, -1, 1, &reply, sizeof(reply) );
return 1;
}
/* timeout for the current thread */
void thread_timeout(void)
{
struct select_reply reply;
assert( current->wait );
reply.signaled = STATUS_TIMEOUT;
end_wait( current );
send_reply( current, -1, 1, &reply, sizeof(reply) );
}
/* attempt to wake threads sleeping on the object wait queue */
void wake_up( struct object *obj, int max )
{
struct wait_queue_entry *entry = obj->head;
while (entry)
{
struct wait_queue_entry *next = entry->next;
if (wake_thread( entry->thread ))
{
if (max && !--max) break;
}
entry = next;
}
}
/* kill a thread on the spot */
void kill_thread( struct thread *thread, int exit_code )
{
if (thread->state == TERMINATED) return; /* already killed */
if (thread->unix_pid) kill( thread->unix_pid, SIGTERM );
remove_client( thread->client_fd, exit_code ); /* this will call thread_killed */
}
/* a thread has been killed */
void thread_killed( struct thread *thread, int exit_code )
{
thread->state = TERMINATED;
thread->exit_code = exit_code;
if (thread->wait) end_wait( thread );
abandon_mutexes( thread );
remove_process_thread( thread->process, thread );
wake_up( &thread->obj, 0 );
release_object( thread );
}