/*
 * Server-side thread management
 *
 * Copyright (C) 1998 Alexandre Julliard
 * Copyright (C) 2004 TransGaming Technologies
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <unistd.h>
#include <stdarg.h>

#include "winbase.h"

#include "handle.h"
#include "process.h"
#include "thread.h"
#include "request.h"
#include "user.h"
#include "server_memory.h"

int is_wakeup = 0;

/* thread queues */

struct thread_wait
{
    struct thread_wait     *next;       /* next wait structure for this thread */
    struct thread          *thread;     /* owner thread */
    int                     count;      /* count of objects */
    int                     flags;
    void                   *cookie;     /* magic cookie to return to client */
    struct timeval          timeout;
    struct timeout_user    *user;
    struct wait_queue_entry queues[1];
};

/* asynchronous procedure calls */

struct thread_apc
{
    struct thread_apc  *next;     /* queue linked list */
    struct thread_apc  *prev;
    struct object      *owner;    /* object that queued this apc */
    void               *func;     /* function to call in client */
    enum apc_type       type;     /* type of apc function */
    int                 nb_args;  /* number of arguments */
    void               *args[1];  /* function arguments */
};


/* thread operations */

static void dump_thread( struct object *obj, int verbose );
static int thread_signaled( struct object *obj, struct wait_queue_entry *entry );
static void thread_poll_event( struct object *obj, int event );
static void destroy_thread( struct object *obj );
static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only );

static const struct object_ops thread_ops =
{
    sizeof(struct thread),      /* size */
    dump_thread,                /* dump */
    add_queue,                  /* add_queue */
    remove_queue,               /* remove_queue */
    thread_signaled,            /* signaled */
    no_satisfied,               /* satisfied */
    NULL,                       /* get_poll_events */
    thread_poll_event,          /* poll_event */
    no_get_fd,                  /* get_fd */
#if defined( CONTEXTUAL_FD )
    no_get_ctx_fd,              /* get_ctx_fd */
#endif
    no_flush,                   /* flush */
    no_get_file_info,           /* get_file_info */
    NULL,                       /* queue_async */
    destroy_thread              /* destroy */
};
const struct object_ops* get_this_process_thread_ops(void) { return &thread_ops; }

START_FILE_STATIC_VARIABLES(thread)
  struct thread *booting_thread;
END_FILE_STATIC_VARIABLES(thread)

extern void set_debug_level( int debuglevel );
/* initialize the structure for a newly allocated thread */
inline static void init_thread_structure( struct thread *thread )
{
    int i;

    thread->unix_tid        = 0;  /* not known yet */
    thread->id              = INVALID_RESOURCE_ID; /* not known yet */
    thread->context         = NULL;
    thread->teb             = NULL;
    thread->mutex           = NULL;
    thread->debug_ctx       = NULL;
    thread->debug_event     = NULL;
    thread->queue           = NULL;
    thread->info            = NULL;
    thread->wait            = NULL;
    thread->system_apc.head = NULL;
    thread->system_apc.tail = NULL;
    thread->user_apc.head   = NULL;
    thread->user_apc.tail   = NULL;
    thread->error           = 0;
    thread->req_data        = NULL;
    thread->req_toread      = 0;
    thread->reply_data      = NULL;
    thread->reply_towrite   = 0;
    thread->reply_fd        = -1;
    init_context_fd( &thread->wait_fd, -1 );
    thread->state           = RUNNING;
    thread->attached        = 0;
    thread->exit_code       = 0;
    thread->next            = NULL;
    thread->prev            = NULL;
    thread->priority        = THREAD_PRIORITY_NORMAL;
    thread->affinity        = 1;
    thread->suspend         = 0;
    thread->id              = INVALID_RESOURCE_ID;

#if !defined(NO_SHARED_MEMORY)
    thread->timeout_head = NULL; /* PH: FIXME: Should walk this at end if DUMP_OBJECTS */
    thread->timeout_tail = NULL;
#endif

    thread->pollfd_array = NULL;
    thread->pollfd_array_size = 0;
    thread->active_users = 0;

#if defined( FAST_VAR_DATA )
    thread->req_vdata = NULL;
    thread->req_maxvdata = 0;
#endif

    for (i = 0; i < MAX_INFLIGHT_FDS; i++)
        thread->inflight[i].server = thread->inflight[i].client = -1;
}

/* create a new thread */
struct thread *create_thread( int fd, struct process *process )
{
    struct thread *thread;

    if (!(thread = alloc_object( thread_ops_tag, fd ))) return NULL;

    init_thread_structure( thread );

    thread->process = (struct process *)grab_object( process );
    thread->request_fd = fd;
    if (!current) current = thread;

    if (!SERVER_STATIC_VARIABLE(thread,booting_thread))  /* first thread ever */
    {
        SERVER_STATIC_VARIABLE(thread,booting_thread) = thread;
        lock_master_socket(1);
    }

    thread->id = create_thread_id ( thread );

    if ((thread->next = SERVER_GLOBAL_VARIABLE( first_thread )) != NULL) thread->next->prev = thread;
    SERVER_GLOBAL_VARIABLE( first_thread ) = thread;

    set_select_events( &thread->obj, POLLIN, NULL );  /* start listening to events */
    add_process_thread( thread->process, thread );
    return thread;
}

/* open a handle to a thread */
DECL_HANDLER(open_thread)
{
    struct thread *thread = get_thread_from_id( req->tid );
    reply->handle = 0;
    if (thread)
    {
        reply->handle = alloc_handle( current->process, thread, req->access, req->inherit );
        release_object( thread );
    }
}


/* handle a client event */
static void thread_poll_event( struct object *obj, int event )
{
    struct thread *thread = (struct thread *)obj;
    assert( obj->ops == thread_ops_tag );

    if (event & (POLLERR | POLLHUP)) kill_thread( thread, 0 );
    else if (event & POLLIN) read_request( thread );
    else if (event & POLLOUT) write_reply( thread );
}

/* cleanup everything that is no longer needed by a dead thread */
/* used by destroy_thread and kill_thread */
static void cleanup_thread( struct thread *thread )
{
    int i;
    struct thread_apc *apc;

    while ((apc = thread_dequeue_apc( thread, 0 ))) mem_free( apc );
    if (thread->req_data) mem_free( thread->req_data );
    if (thread->reply_data) mem_free( thread->reply_data );
    if (thread->request_fd != -1) close( thread->request_fd );
    if (thread->reply_fd != -1) close( thread->reply_fd );
    if( thread->wait_fd != -1 ) release_context_fd( thread->wait_fd, NULL, NULL );
    if (thread->queue)
    {
        if (thread->process->queue == thread->queue)
        {
            release_object( thread->process->queue );
            thread->process->queue = NULL;
        }
        release_object( thread->queue );
        thread->queue = NULL;
    }
    destroy_thread_windows( thread );
    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;
        }
    }
    thread->req_data = NULL;
    thread->reply_data = NULL;
    thread->request_fd = -1;
    thread->reply_fd = -1;
    thread->wait_fd = INVALID_CTX_FD;

    if( thread->pollfd_array ) mem_free( thread->pollfd_array );
    thread->pollfd_array = NULL;
    thread->pollfd_array_size = 0;
    thread->active_users = 0;  
}

/* destroy a thread when its refcount is 0 */
static void destroy_thread( struct object *obj )
{
    struct thread_apc *apc;
    struct thread *thread = (struct thread *)obj;
    assert( obj->ops == thread_ops_tag );

    assert( !thread->debug_ctx );  /* cannot still be debugging something */

    if (thread->id != INVALID_RESOURCE_ID ) 
    {
       remove_thread_id ( thread->id );
       thread->id = INVALID_RESOURCE_ID;
    }

    if (thread->next) thread->next->prev = thread->prev;
    if (thread->prev) thread->prev->next = thread->next;
    else SERVER_GLOBAL_VARIABLE( first_thread ) = thread->next;
    while ((apc = thread_dequeue_apc( thread, 0 ))) mem_free( apc );
    if (thread->info) release_object( thread->info );
    cleanup_thread( thread );
    release_object( thread->process );
}

/* 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_tag );

    fprintf( stderr, "Thread %04x unix tid=%d teb=%p state=%d process=%04x\n",
             get_thread_id( thread ), thread->unix_tid, thread->teb, thread->state, get_process_id( thread->process ) );
}

static int thread_signaled( struct object *obj, struct wait_queue_entry *entry )
{
    struct thread *mythread = (struct thread *)obj;
    return (mythread->state == TERMINATED);
}



/* get a thread from a handle (and increment the refcount) */
struct thread *get_thread_from_handle( handle_t handle, unsigned int access )
{
    return (struct thread *)get_handle_obj( current->process, handle,
                                            access, thread_ops_tag );
}

/* find a thread from a Unix tid */
struct thread *get_thread_from_tid( int tid )
{
    struct thread *t = SERVER_GLOBAL_VARIABLE( first_thread );
    while (t && (t->unix_tid != tid)) t = t->next;
    return t;
}

/* set all information about a thread */
static void set_thread_info( struct thread *thread,
                             const struct set_thread_info_request *req )
{
    if (req->mask & SET_THREAD_INFO_PRIORITY)
        thread->priority = req->priority;
    if (req->mask & SET_THREAD_INFO_AFFINITY)
    {
        if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER );
        else thread->affinity = req->affinity;
    }
}

/* suspend a thread */
int suspend_thread( struct thread *thread, int check_limit )
{
    int old_count = thread->suspend;
    if (thread->suspend < MAXIMUM_SUSPEND_COUNT || !check_limit)
    {
        if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread );
    }
    else set_error( STATUS_SUSPEND_COUNT_EXCEEDED );
    return old_count;
}

/* resume a thread */
int resume_thread( struct thread *thread )
{
    int old_count = thread->suspend;
    if (thread->suspend > 0)
    {
        if (!(--thread->suspend + thread->process->suspend))
        {
            continue_thread( thread );
            wake_thread( thread );
        }
    }
    return old_count;
}

/* 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++)
        get_ops(entry->obj->ops)->remove_queue( entry->obj, entry );
    if (wait->user) remove_timeout_user( wait->user );
    thread->wait = wait->next;
    mem_free( wait );
}

/* build the thread wait structure */
static int wait_on( int count, struct object *objects[], int flags, int sec, int usec )
{
    struct thread_wait *wait;
    struct wait_queue_entry *entry;
    int i;

    if (!(wait = mem_alloc( sizeof(*wait) + (count-1) * sizeof(*entry) ))) return 0;

    /* Although it doesn't make much sense at first, it's possible to be making a
     * wineserver call when waiting to be unblocked to a previous request. Ie
     * thread A blocks on for semaphore then allocates memory before waiting and that
     * requires a semaphore so it must wait again.
     * This could result in an infinite loop if, for instance, the HeapAlloc
     * lock was required and caused the a wait, which then required the HeapAlloc
     * lock to setup the wait, which caused the HeapAlloc lock. However, this should
     * be avoidable by correctly using the Alertable state.
     * However, APC requires this since we cannot control this. <grumble>
     */
    wait->next    = current->wait;
    wait->thread  = current;
    wait->count   = count;
    wait->flags   = flags;
    wait->user    = NULL;

    current->wait = wait;

    if (flags & SELECT_TIMEOUT)
    {
        wait->timeout.tv_sec = sec;
        wait->timeout.tv_usec = usec;
    }

    for (i = 0, entry = wait->queues; i < count; i++, entry++)
    {
        struct object *obj = objects[i];
        entry->thread = current;
        entry->poll_thread = is_wineserver ? NULL : current;
        if (!get_ops(obj->ops)->add_queue( obj, entry ))
        {
            wait->count = i;
            end_wait( current );
            return 0;
        }
    }

    return 1;
}

/* check if the thread waiting condition is satisfied */
static int check_wait( struct thread *thread )
{
    int i, signaled;
    struct thread_wait *wait = thread->wait;
    struct wait_queue_entry *entry = wait->queues;
    
    /* Suspended threads may not acquire locks */
    if( thread->process->suspend + thread->suspend > 0 ) return -1;

    assert( wait );
    if (wait->flags & SELECT_ALL)
    {
        int not_ok = 0;
        /* Note: we must check them all anyway, as some objects may
         * want to do something when signaled, even if others are not */
        for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
            not_ok |= !get_ops(entry->obj->ops)->signaled( entry->obj, entry );
        if (not_ok) goto other_checks;
        /* Wait satisfied: tell it to all objects */
        signaled = 0;
        for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
            if (get_ops(entry->obj->ops)->satisfied( entry->obj, thread ))
                signaled = STATUS_ABANDONED_WAIT_0;
        return signaled;
    }
    else
    {
        for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
        {
            if (!get_ops(entry->obj->ops)->signaled( entry->obj, entry )) continue;
            /* Wait satisfied: tell it to the object */
            signaled = i;
            if (get_ops(entry->obj->ops)->satisfied( entry->obj, thread ))
                signaled = i + STATUS_ABANDONED_WAIT_0;
            return signaled;
        }
    }

 other_checks:
    if ((wait->flags & SELECT_INTERRUPTIBLE) && thread->system_apc.head) return STATUS_USER_APC;
    if ((wait->flags & SELECT_ALERTABLE) && thread->user_apc.head) return STATUS_USER_APC;
    if (wait->flags & SELECT_TIMEOUT)
    {
        struct timeval now;
        gettimeofday( &now, NULL );
        if (!time_before( &now, &wait->timeout )) return STATUS_TIMEOUT;
    }
    return -1;
}


/* send the wakeup signal to a thread */
static int send_thread_wakeup( struct thread *thread, void *cookie, int signaled )
{
    struct wake_up_reply reply;
    int ret;

    reply.cookie   = cookie;
    reply.signaled = signaled;
    if ((ret = write( get_context_fd( thread->wait_fd ), &reply, sizeof(reply) )) == sizeof(reply))
    {
       return 0;
    }

    if (ret >= 0)
    {
        fatal_protocol_error( thread, "partial wakeup write %d\n", ret );
    }
    else if (errno == EPIPE)
    {
        kill_thread( thread, 0 );  /* normal death */
    }
    else
    {
        info_msg( ALWAYS, thread, "send_thread_wakeup failed with errno %d\n", errno );
        fatal_protocol_perror( thread, "write" );
    }

    return -1;
}

int is_cookie_satisfied( const struct thread* thread, const void* cookie )
{
  const struct thread_wait* wait = thread->wait;

  while( wait )
  {
     if( wait->cookie == cookie ) return 0;
     wait = wait->next;
  }

  /* Can't find it... must be satisfied. */
  return 1;
}

/* attempt to wake up a thread */
/* return >0 if OK, 0 if the wait condition is still not satisfied */
int wake_thread( struct thread *thread )
{
    int signaled, count;
    void *cookie;

    for (count = 0; thread->wait; count++)
    {
        if ((signaled = check_wait( thread )) == -1) break;

        cookie = thread->wait->cookie;
        info_msg( WITH_DEBUG, thread, "*wakeup* signaled=%d cookie=%p\n", signaled, cookie );
        end_wait( thread );
        if (send_thread_wakeup( thread, cookie, signaled ) == -1) /* error */
            break;
    }
    return count;
}

/* thread wait timeout */
static void thread_timeout( void *ptr )
{
    struct thread_wait *wait = ptr;
    struct thread *thread = wait->thread;
    void *cookie = wait->cookie;

    wait->user = NULL;
    if (thread->wait != wait) return; /* not the top-level wait, ignore it */

    is_wakeup = 1;

    info_msg( WITH_DEBUG, thread, "*wakeup* signaled=%d cookie=%p\n", STATUS_TIMEOUT, cookie );

    end_wait( thread );

    send_thread_wakeup( thread, cookie, STATUS_TIMEOUT );

    /* check if other objects have become signaled in the meantime */
    wake_thread( thread );

    is_wakeup = 0;
}
timeout_callback get_thread_thread_timeout_callback(void) { return thread_timeout; }

extern int set_handle_in_place( struct process* process, handle_t handle, context_fd ctx_fd );

/* select on a list of handles */
static void select_on( int count, void *cookie, const handle_t *handles,
                       int flags, int sec, int usec, struct object* objs[] )
{
    int ret, i;
    struct object *objects[MAXIMUM_WAIT_OBJECTS];

    if ((count < 0) || (count > MAXIMUM_WAIT_OBJECTS))
    {
        set_error( STATUS_INVALID_PARAMETER );
        return;
    }

    for (i = 0; i < count; i++)
    {
        context_fd ctx_fd;

        if (!(objects[i] = get_handle_obj( current->process, handles[i], SYNCHRONIZE, NULL )))
            break;

        if( objs )
        {
            ctx_fd = get_ops( objects[i]->ops )->get_ctx_fd( objects[i] );
            if( ctx_fd != INVALID_CTX_FD )
            {
                /* If there is something associated, make sure we have an fd */
                if( set_handle_in_place( current->process, handles[i], ctx_fd ) == -1 )
                    break;
            }
            /* Dirty copy but safe because shm call has outstanding handles */
            /* PH: FIXME: Should hold references? Danger that thread is killed holding them */
            objs[i+1] = objects[i];
        }
    }

    if (i < count) goto done;

    if (!wait_on( count, objects, flags, sec, usec )) goto done;

    if ((ret = check_wait( current )) != -1)
    {
        /* condition is already satisfied */
        end_wait( current );
        set_error( ret );
        goto done;
    }

    /* now we need to wait */
    if (flags & SELECT_TIMEOUT)
    {
        if (!(current->wait->user = add_timeout_user( &current->wait->timeout,
                                                      thread_thread_timeout_callback_tag,
                                                      current->wait )))
        {
            end_wait( current );
            goto done;
        }
    }

    current->wait->cookie = cookie;
    set_error( STATUS_PENDING );

done:
    while (--i >= 0) release_object( objects[i] );
}

/* 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;

is_wakeup = 1;
//    fprintf( stderr, "obj=%p entry=(%p)->(obj=%p)\n", obj, entry, entry ? entry->obj : NULL);


    while (entry)
    {
        struct thread *thread = entry->thread;
       // fprintf( stderr, "thread=%p entry=(%p)->(obj=%p,next=%p)\n",
        //                 thread, entry, entry ? entry->obj : NULL, entry ? entry->next : NULL );
        entry = entry->next;
        //fprintf( stderr, "Next entry=(%p)->(obj=%p,next=%p, thread=%p)\n",
          //               entry, entry ? entry->obj : NULL, entry ? entry->next : NULL,
            //             entry ? entry->thread : NULL );
        if (wake_thread( thread ))
        {
            if (max && !--max) break;
        }
         
        /* Get it from the new head of the list */
       // entry = obj->head; This seems more correct to me since entry could be deleted already.
    }

is_wakeup = 0; /* PH: FIXME: Ugly */
}

/* queue an async procedure call */
int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
                      enum apc_type type, int system, int nb_args, ... )
{
    struct thread_apc *apc;
    struct apc_queue *queue = system ? &thread->system_apc : &thread->user_apc;

    /* cancel a possible previous APC with the same owner */
    if (owner) thread_cancel_apc( thread, owner, system );

    if (!(apc = mem_alloc( sizeof(*apc) + (nb_args-1)*sizeof(apc->args[0]) ))) return 0;

    apc->prev    = queue->tail;
    apc->next    = NULL;
    apc->owner   = owner;
    apc->func    = func;
    apc->type    = type;
    apc->nb_args = nb_args;
    if (nb_args)
    {
        int i;
        va_list args;
        va_start( args, nb_args );
        for (i = 0; i < nb_args; i++) apc->args[i] = va_arg( args, void * );
        va_end( args );
    }
    queue->tail = apc;
    if (!apc->prev)  /* first one */
    {
        queue->head = apc;
        wake_thread( thread );
    }
    else apc->prev->next = apc;

    return 1;
}

/* cancel the async procedure call owned by a specific object */
void thread_cancel_apc( struct thread *thread, struct object *owner, int system )
{
    struct thread_apc *apc;
    struct apc_queue *queue = system ? &thread->system_apc : &thread->user_apc;
    for (apc = queue->head; apc; apc = apc->next)
    {
        if (apc->owner != owner) continue;
        if (apc->next) apc->next->prev = apc->prev;
        else queue->tail = apc->prev;
        if (apc->prev) apc->prev->next = apc->next;
        else queue->head = apc->next;
        mem_free( apc );
        return;
    }
}

/* remove the head apc from the queue; the returned pointer must be freed by the caller */
static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only )
{
    struct thread_apc *apc;
    struct apc_queue *queue = &thread->system_apc;

    if (!queue->head && !system_only) queue = &thread->user_apc;
    if ((apc = queue->head))
    {
        if (apc->next) apc->next->prev = NULL;
        else queue->tail = NULL;
        queue->head = apc->next;
    }
    return apc;
}

/* 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 )
{
    int i;

    if (server == -1) return -1;
    if (client == -1)
    {
        close( server );
        return -1;
    }

    /* 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;
        }

    /* 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;
        }
    return -1;
}

/* 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;

    if (client == -1) return -1;

    do
    {
        for (i = 0; i < MAX_INFLIGHT_FDS; i++)
        {
            if (thread->inflight[i].client == client)
            {
                ret = thread->inflight[i].server;
                thread->inflight[i].server = thread->inflight[i].client = -1;
                return ret;
            }
        }
    } while (!receive_fd( thread->process ));  /* in case it is still in the socket buffer */
    return -1;
}

/* retrieve an LDT selector entry */
static void get_selector_entry( struct thread *thread, int entry,
                                unsigned int *base, unsigned int *limit,
                                unsigned char *flags )
{
    if (!thread->process->ldt_copy)
    {
        set_error( STATUS_ACCESS_DENIED );
        return;
    }
    if (entry >= 8192)
    {
        set_error( STATUS_INVALID_PARAMETER );  /* FIXME */
        return;
    }
    if (suspend_for_ptrace( thread ))
    {
	size_t num_read;
        unsigned char flags_buf[4];
        uintptr_t addr = (uintptr_t)((uint32_t*)thread->process->ldt_copy + entry);
        if (read_thread_data( thread, addr, sizeof(*base), base,
			      &num_read ) == -1)
	    goto done;
        if (read_thread_data( thread, addr + 8192, sizeof(*limit),
			     limit, &num_read ) == -1) goto done;
        addr = (uintptr_t)((uint32_t*)thread->process->ldt_copy + 2*8192 + (entry >> 2));
        if (read_thread_data( thread, addr, sizeof(flags_buf),
			      flags_buf, &num_read ) == -1) goto done;
        *flags = flags_buf[entry & 3];
    done:
        resume_thread( thread );
    }
}

int is_thread_death = 0;

/* kill a thread on the spot */
void kill_thread( struct thread *thread, int violent_death )
{
    if (thread->state == TERMINATED) return;  /* already killed */

    is_thread_death = 1;

    thread->state = TERMINATED;
    if (current == thread) current = NULL;
    info_msg( WITH_DEBUG, thread, "*killed* exit_code=%d/%p\n", thread->exit_code, thread->wait );
    if (thread->wait)
    {
        while (thread->wait) end_wait( thread );
        send_thread_wakeup( thread, NULL, STATUS_PENDING );
        /* if it is waiting on the socket, we don't need to send a SIGTERM */
        violent_death = 0;
    }
    kill_console_processes( thread, 0 );
    debug_exit_thread( thread );
    abandon_mutexes( thread );
    remove_process_thread( thread->process, thread );
    wake_up( &thread->obj, 0 );
    detach_thread( thread, violent_death ? SIGTERM : 0 );

    if (thread->request_fd == get_context_untranslated_fd( thread->obj.fd, NULL ))
        thread->request_fd = -1;
    if (thread->reply_fd == get_context_untranslated_fd( thread->obj.fd, NULL ) )
        thread->reply_fd = -1;

    remove_select_user( &thread->obj );
    cleanup_thread( thread );
    release_object( thread );

    is_thread_death = 0;
}

/* take a snapshot of currently running threads */
struct thread_snapshot *thread_snap( int *count )
{
    struct thread_snapshot *snapshot, *ptr;
    struct thread *thread;
    int total = 0;

    for (thread = SERVER_GLOBAL_VARIABLE( first_thread ); thread; thread = thread->next)
        if (thread->state != TERMINATED) total++;
    if (!total || !(snapshot = mem_alloc( sizeof(*snapshot) * total ))) return NULL;
    ptr = snapshot;
    for (thread = SERVER_GLOBAL_VARIABLE( first_thread ); thread; thread = thread->next)
    {
        if (thread->state == TERMINATED) continue;
        ptr->thread   = thread;
        ptr->count    = thread->obj.refcount;
        ptr->priority = thread->priority;
        grab_object( thread );
        ptr++;
    }
    *count = total;
    return snapshot;
}



/* signal that we are finished booting on the client side */
DECL_HANDLER(boot_done)
{
    set_debug_level( max( debug_level, req->debug_level ) );
    if (current == SERVER_STATIC_VARIABLE(thread,booting_thread))
    {
        SERVER_STATIC_VARIABLE(thread,booting_thread) = (struct thread *)~0UL;  /* make sure it doesn't match other threads */
        lock_master_socket(0);  /* allow other clients now */
    }
}

/* enable trace */
DECL_HANDLER(enable_trace)
{
    debug_level = req->debug_level;
}

/* create a new thread */
DECL_HANDLER(new_thread)
{
    struct thread *thread;
    int request_fd = thread_get_inflight_fd( current, req->request_fd );

    if (request_fd == -1 || fcntl( request_fd, F_SETFL, O_NONBLOCK ) == -1)
    {
        if (request_fd != -1) close( request_fd );
        set_error( STATUS_INVALID_HANDLE );
        return;
    }

    if ((thread = create_thread( request_fd, current->process )))
    {
        if (req->suspend) thread->suspend++;
        reply->tid = get_thread_id( thread );
        if ((reply->handle = alloc_handle( current->process, thread,
                                           THREAD_ALL_ACCESS, req->inherit )))
        {
            /* thread object will be/released when the thread gets killed */
            return;
        }
        kill_thread( thread, 1 );
        request_fd = -1;
    }
}

/* initialize a new thread */
DECL_HANDLER(init_thread)
{
    int reply_fd = thread_get_inflight_fd( current, req->reply_fd );
    int wait_fd = thread_get_inflight_fd( current, req->wait_fd );

    if (current->unix_tid)
    {
        fatal_protocol_error( current, "init_thread: already running\n" );
        goto error;
    }
    if (reply_fd == -1 || fcntl( reply_fd, F_SETFL, O_NONBLOCK ) == -1)
    {
        fatal_protocol_error( current, "bad reply fd\n" );
        goto error;
    }
    if (wait_fd == -1)
    {
        fatal_protocol_error( current, "bad wait fd\n" );
        goto error;
    }

    current->unix_tid = req->unix_tid;
    current->teb      = req->teb;
    current->reply_fd = reply_fd;
    set_context_fd( &current->wait_fd, wait_fd );
    grab_context_fd( current->wait_fd, NULL );
    current->module_status = THREAD_MODULE_INIT;

    if (current->suspend + current->process->suspend > 0) stop_thread( current );
    if (current->process->running_threads > 1)
        generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, req->entry );

    reply->pid                  = get_process_id( current->process );
    reply->tid                  = get_thread_id( current );
    reply->boot                 = (current == SERVER_STATIC_VARIABLE(thread,booting_thread));
    reply->version              = SERVER_PROTOCOL_VERSION;

    return;

 error:
    if (reply_fd != -1) close( reply_fd );
    if (wait_fd != -1) close( wait_fd );
}

/* set module load status */
DECL_HANDLER(set_thread_moduleload)
{
    current->module_status = req->status;
}

DECL_HANDLER(get_thread_moduleload)
{
    struct thread *thread = get_thread_from_handle( req->handle, THREAD_ALL_ACCESS );
    if( thread )
    {
        reply->status = thread->module_status;
        reply->self = (thread->id == current->id);
        release_object( thread );
    }
}

/* terminate a thread */
DECL_HANDLER(terminate_thread)
{
    struct thread *thread;

    reply->self = 0;
    reply->last = 0;
    if ((thread = get_thread_from_handle( req->handle, THREAD_TERMINATE )))
    {
        thread->exit_code = req->exit_code;
        if (thread != current) kill_thread( thread, 1 );
        else
        {
            reply->self = 1;
            reply->last = (thread->process->running_threads == 1);
        }
        release_object( thread );
    }
}

/* fetch information about a thread */
DECL_HANDLER(get_thread_info)
{
    struct thread *thread;
    handle_t handle = req->handle;

    if (!handle) 
       thread = get_thread_from_id( req->tid_in );
    else thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION );

    if (thread)
    {
        reply->tid       = get_thread_id( thread );
        reply->teb       = thread->teb;
        reply->exit_code = (thread->state == TERMINATED) ? thread->exit_code : STILL_ACTIVE;
        reply->priority  = thread->priority;
        release_object( thread );
    }
}

/* set information about a thread */
DECL_HANDLER(set_thread_info)
{
    struct thread *thread;

    if ((thread = get_thread_from_handle( req->handle, THREAD_SET_INFORMATION )))
    {
        set_thread_info( thread, req );
        release_object( thread );
    }
}

/* suspend a thread */
DECL_HANDLER(suspend_thread)
{
    struct thread *thread;

    if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME )))
    {
        reply->count = suspend_thread( thread, 1 );
        release_object( thread );
    }
}

/* resume a thread */
DECL_HANDLER(resume_thread)
{
    struct thread *thread;

    if ((thread = get_thread_from_handle( req->handle, THREAD_SUSPEND_RESUME )))
    {
        reply->count = resume_thread( thread );
	if( reply->count == 1 ) wake_thread( thread );	
        release_object( thread );
    }
}


/* select on a handle list */
DECL_HANDLER(select)
{
    int count = get_req_data_size() / sizeof(int);
    select_on( count, req->cookie, get_req_data(), req->flags, req->sec, req->usec, NULL );
}

/* select on a handle list */
DECL_HANDLER(shm_select)
{
    extern int is_select;
    struct shm_pollfd* pfd = (struct shm_pollfd*)req->pollfd;

    int count = get_req_data_size() / sizeof(int);

    is_select = 1;

    select_on( count, req->cookie, get_req_data(), req->flags, req->sec, req->usec, (struct object**)pfd->objects );

    if( get_error() == STATUS_PENDING ) update_pollfd_for_thread( pfd, &reply->pollfds, count );
    else reply->pollfds = 0;

    is_select = 0;
}

/* queue an APC for a thread */
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, 1, req->param );
        release_object( thread );
    }
}

/* get next APC to call */
DECL_HANDLER(get_apc)
{
    struct thread_apc *apc;
    size_t size;

    for (;;)
    {
        if (!(apc = thread_dequeue_apc( current, !req->alertable )))
        {
            /* no more APCs */
            reply->func = NULL;
            reply->type = APC_NONE;
            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.
         */
        if (apc->func || apc->type == APC_ASYNC_IO ) break;
        mem_free( apc );
    }
    size = apc->nb_args * sizeof(apc->args[0]);
    if (size > get_reply_max_size()) size = get_reply_max_size();
    reply->func = apc->func;
    reply->type = apc->type;
    set_reply_data( apc->args, size );
    mem_free( apc );
}

/* fetch a selector entry for a thread */
DECL_HANDLER(get_selector_entry)
{
    struct thread *thread;
    if ((thread = get_thread_from_handle( req->handle, THREAD_QUERY_INFORMATION )))
    {
        get_selector_entry( thread, req->entry, &reply->base, &reply->limit, &reply->flags );
        release_object( thread );
    }
}

DECL_HANDLER(update_thread_tid)
{
    if( current->unix_tid != req->old_unix_tid )
        fatal_protocol_error( current, "Invalid tid update\n" );

    current->unix_tid = req->new_unix_tid; 
}

/* The brains behind get_thread_id and get_process_id */
unsigned int get_resource_id (struct object *objptr, struct thread_process_id_list **server_idlist, struct thread_process_id_list **server_first_free_objid,
                              struct thread_process_id_list **server_last_free_objid)
{
   struct thread_process_id_list *obj_list = *server_idlist;
   unsigned int highest_id_so_far = 0; 
   struct thread_process_id_list *first_free_objid = *server_first_free_objid;
   struct thread_process_id_list *last_free_objid = *server_last_free_objid;


   /* If the thread ids variable is NULL, then this is the first thread that
      is being created */
   if (!obj_list)
   {
      obj_list = *server_idlist = mem_alloc( sizeof(struct thread_process_id_list)  );  
      if (!obj_list)
      {
         /* Memory allocation failed  */
         fprintf(stderr, "server: err: get_resource_id: Error allocating memory!\n");
         set_error(STATUS_NO_MEMORY);
         return INVALID_RESOURCE_ID;
      }
   }
   else
   {
      int keep_going = 1;


      /* Go through the list and see if we can find our 
         server object */
      while(keep_going)
      {
         /* If we have a match, just return the ID */
         if (obj_list->obj_ptr == objptr)
         {
            return (obj_list->obj_id);
         }
         
         /* If the current ID is greater than the current highest one, 
            we need to grab that ID. This is because smaller IDs 
            can come in later in the list as IDs get re-used.. */
         if (obj_list->obj_id > highest_id_so_far)
         {
            highest_id_so_far = obj_list->obj_id;
         }

         if (obj_list->next)
            obj_list = obj_list->next;
         else
            keep_going = 0;
      }

      /* Its a new entry - Allocate the memory for the entry */
      obj_list->next = mem_alloc( sizeof(struct thread_process_id_list)  );  
      if (!obj_list->next)
      {
         /* Memory allocation failed  */
         fprintf(stderr, "server: err: get_resource_id: Error allocating memory!\n");
         set_error(STATUS_NO_MEMORY);
         return INVALID_RESOURCE_ID;
      }
      
      /* From here on we only care about the new entry */
      obj_list = obj_list->next;
   }


   /* Blank out the new entry, and set up the pointer to the thread/process struct itself */
   memset(obj_list, 0, sizeof(struct thread_process_id_list));
   obj_list->obj_ptr = objptr;

   /* If we have any old entries in the list of free IDs, we should re-use it first */
   if ( first_free_objid )
   {
      obj_list->obj_id = first_free_objid->obj_id;

      /* If first and last are the same, then we are taking the last free entry.
         Free the memory and set the first and last to empty */
      if (first_free_objid == last_free_objid)
      {
         mem_free(first_free_objid);
         *server_first_free_objid = *server_last_free_objid = NULL;
      }
      else
      {
         /* There are more entries... set link list appropriately */
         struct thread_process_id_list *temp_entry = first_free_objid->next;
         mem_free(first_free_objid);
         *server_first_free_objid = temp_entry;
      }
   }
   else
   {
      highest_id_so_far = highest_id_so_far + 1;
      obj_list->obj_id = highest_id_so_far;

      /* Make sure the new id doesn't blow our limit */
      if (highest_id_so_far > MAX_RESOURCE_IDS)
      {
         fprintf(stderr, "server: err: get_resource_id: too many resources (threads/processed) created - resource id cannot exceed 16 bits\n");
         set_error(STATUS_NO_MEMORY);
         return INVALID_RESOURCE_ID;
      }
   }

   return obj_list->obj_id;
}

/* The brains behind get_thread_from_id / get_process_from_id */
struct object *get_resource_from_id (const unsigned int id, struct thread_process_id_list **server_idlist)
{
    struct thread_process_id_list *obj_list = *server_idlist;


    while (obj_list)
    {
       if (obj_list->obj_id == id)
       {
          grab_object (obj_list->obj_ptr); // PH: FIXME: Can't be const.
          return obj_list->obj_ptr;
       }

       obj_list = obj_list->next;
    }

    set_error( STATUS_INVALID_PARAMETER );
    
    return NULL;
}

/* The brains behind remove_thread_id and remove_process_id */
void remove_resource_id ( unsigned int obj_id, struct thread_process_id_list **server_idlist, struct thread_process_id_list **server_first_free_objid,
                        struct thread_process_id_list **server_last_free_objid)
{
    struct thread_process_id_list *obj_list = *server_idlist;
    struct thread_process_id_list *first_free_objid = *server_first_free_objid;
    struct thread_process_id_list *last_free_objid = *server_last_free_objid;
    struct thread_process_id_list *prev_entry = NULL;
    
    while (obj_list)
    {
       if (obj_list->obj_id == obj_id)
       {
          struct thread_process_id_list *new_free_objid = NULL;

          /* Adjust the previous entry, if there is one */
          if (prev_entry)
          {
             prev_entry->next = obj_list->next;
          }
          else if (obj_list == *server_idlist)
          {
             *server_idlist = obj_list->next;
          }
       
          /* Free the memory */
          mem_free ( obj_list );

          /* Make free thread id entry */
          new_free_objid = mem_alloc ( sizeof (struct thread_process_id_list) );
          if (!new_free_objid)
          {
             fprintf(stderr, "server: ERR: remove_resource_id: Unable to allocate memory for free resource id slot, expect problems!!\n");
             return;
          }

          /* Initialize the new entry */
          new_free_objid->obj_id = obj_id;
          new_free_objid->next = NULL;

          /* Add thread id to the list of free ids */
          if (last_free_objid)
          {
             last_free_objid->next = new_free_objid;
             *server_last_free_objid = new_free_objid;
          }
          else
          {
             /* If there is no last free thread id. then we are the first */
             /* sanity check */
             if (!first_free_objid)
             {
                *server_first_free_objid = *server_last_free_objid = new_free_objid;
             }
             else
             {
                fprintf(stderr, "server: ERR: get_resource_id: We seem to have a first with no last. Should never happen!\n");
             }
          }
          

          /* Done! */
          return;
       }
    
       prev_entry = obj_list;
       obj_list = obj_list->next;
    }
}

/* Function that retrieves the ID for a particular thread. If
   this is a new thread (ie. doesn't have a ID already) it will 
   allocate a new entry in the linked list of thread ids
*/
thread_id_t create_thread_id ( struct thread *thread )
{
   thread_id_t rettid =  get_resource_id ( &thread->obj, &SERVER_GLOBAL_VARIABLE (threadids), &SERVER_GLOBAL_VARIABLE(first_free_threadid), 
                                           &SERVER_GLOBAL_VARIABLE(last_free_threadid) ) ;
   return rettid;
}

/* get a thread pointer from a thread id (and increment the refcount) */
struct thread *get_thread_from_id( const thread_id_t id )
{
   struct thread *ret_thread;
   
   ret_thread = (struct thread*)get_resource_from_id ( id, &SERVER_GLOBAL_VARIABLE( threadids ) );
   return ret_thread;
}


/* Removes the thread id structure from the linked list of other threads that exist in the system. */
void remove_thread_id ( const thread_id_t thread_id )
{
    remove_resource_id ( thread_id, &SERVER_GLOBAL_VARIABLE( threadids ), &SERVER_GLOBAL_VARIABLE( first_free_threadid ),
                         &SERVER_GLOBAL_VARIABLE( last_free_threadid ) );
}
