Added SILC Server library.
[silc.git] / lib / silcutil / win32 / silcwin32thread.c
1 /*
2
3   silcwin32thread.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2005 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12   
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 #ifdef SILC_THREADS
24
25 /* Thread structure for WIN32 */
26 typedef struct {
27   HANDLE thread;
28   SilcThreadStart start_func;
29   void *context;
30   SilcBool waitable;
31 } *SilcWin32Thread;
32
33 static DWORD silc_thread_tls;
34
35 /* Actual routine that is called by WIN32 when the thread is created.
36    We will call the start_func from here. When this returns the thread
37    is destroyed. */
38
39 unsigned __stdcall silc_thread_win32_start(void *context)
40 {
41   SilcWin32Thread thread = (SilcWin32Thread)context;
42
43   TlsSetValue(silc_thread_tls, context);
44   silc_thread_exit(thread->start_func(thread->context));
45
46   return 0;
47 }
48 #endif
49
50 SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
51                               SilcBool waitable)
52 {
53 #ifdef SILC_THREADS
54   SilcWin32Thread thread;
55   unsigned id;
56
57   SILC_LOG_DEBUG(("Creating new thread"));
58
59   thread = silc_calloc(1, sizeof(*thread));
60   thread->start_func = start_func;
61   thread->context = context;
62   thread->waitable = waitable;
63   thread->thread =
64     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)silc_thread_win32_start,
65                  (void *)thread, 0, &id);
66
67   if (!thread->thread) {
68     SILC_LOG_ERROR(("Could not create new thread"));
69     silc_free(thread);
70     return NULL;
71   }
72
73   return (SilcThread)thread;
74 #else
75   /* Call thread callback immediately */
76   (*start_func)(context);
77   return NULL;
78 #endif
79 }
80
81 void silc_thread_exit(void *exit_value)
82 {
83 #ifdef SILC_THREADS
84   SilcWin32Thread thread = TlsGetValue(silc_thread_tls);
85   
86   if (thread) {
87     /* If the thread is waitable the memory is freed only in silc_thread_wait
88        by another thread. If not waitable, free it now. */
89     if (!thread->waitable) {
90       TerminateThread(thread->thread, 0);
91       silc_free(thread);
92     }
93
94     TlsSetValue(silc_thread_tls, NULL);
95   }
96   ExitThread(0);
97 #endif
98 }
99
100 SilcThread silc_thread_self(void)
101 {
102 #ifdef SILC_THREADS
103   SilcWin32Thread self = TlsGetValue(silc_thread_tls);
104
105   if (!self) {
106     /* This should only happen for the main thread! */
107     HANDLE handle = GetCurrentThread ();
108     HANDLE process = GetCurrentProcess ();
109     self = silc_calloc(1, sizeof(*self));
110     DuplicateHandle(process, handle, process, 
111                     &self->thread, 0, FALSE, 
112                     DUPLICATE_SAME_ACCESS);
113     TlsSetValue(silc_thread_tls, self);
114   }
115
116   return (SilcThread)self;
117         #else
118   return NULL;
119 #endif
120 }
121
122 SilcBool silc_thread_wait(SilcThread thread, void **exit_value)
123 {
124 #ifdef SILC_THREADS
125   SilcWin32Thread self = (SilcWin32Thread)thread;
126
127   SILC_LOG_DEBUG(("Waiting for thread %p", self));
128
129   if (!self->waitable)
130     return FALSE;
131
132   /* The thread is waitable thus we will free all memory after the
133      WaitForSingleObject returns, the thread is destroyed after that. */
134   if (WaitForSingleObject(self->thread, 2500) == WAIT_TIMEOUT)
135     TerminateThread(self->thread, 0);
136
137   silc_free(self);
138   if (exit_value)
139     *exit_value = NULL;
140
141   return TRUE;
142 #else
143   return FALSE;
144 #endif
145 }