1 /* vi:ai:et:ts=8 sw=2
2 */
3 /*
4 * wzdftpd - a modular and cool ftp server
5 * Copyright (C) 2002-2004 Pierre Chifflier
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * As a special exemption, Pierre Chifflier
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26 /** \file wzd_threads.c
27 * \brief Threads implementation
28 * \warning This file contains many platform-dependant code
29 */
30
31 #include "wzd_all.h"
32
33 #ifndef WZD_USE_PCH
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/types.h>
40
41 #ifdef WIN32
42 # define _WIN32_WINNT 0x500
43 # include <windows.h>
44 #else
45 #include <unistd.h>
46
47 #include <signal.h>
48 #endif
49
50 struct wzd_context_t;
51
52 #include "wzd_log.h"
53 #include "wzd_threads.h"
54
55 #include "wzd_debug.h"
56
57 #else /* WZD_USE_PCH */
58 #ifdef WIN32
59 #define _WIN32_WINNT 0x500
60 #include <windows.h>
61 #endif
62 #endif /* WZD_USE_PCH */
63
64 #ifdef HAVE_PTHREAD
65
66 struct thread_key_t {
67 pthread_key_t key;
68 };
69
70 #else /* HAVE_PTHREAD */
71
72 struct thread_key_t {
73 DWORD key;
74 };
75
76 #endif /* HAVE_PTHREAD*/
77
78 /** \brief Create a new thread
79 *
80 * This function create a new thread, using native threads on Windows and pthreads
81 * elsewhere. The new thread is started immediatly, unless specific attributes
82 * have been defined.
83 *
84 * \param[out] thread location where the new thread id will be stored
85 * \param[in] attr thread specific attributes
86 * \param[in] start_routine the function to execute in the new thread
87 * \param[in] arg an argument to be passed to start_routine
88 */
wzd_thread_create(wzd_thread_t * thread,wzd_thread_attr_t * attr,void * (start_routine)(void *),void * arg)89 int wzd_thread_create(wzd_thread_t * thread, wzd_thread_attr_t * attr, void * (start_routine)(void *), void * arg)
90 {
91 #ifndef WIN32
92 int ret;
93
94 {
95 /* block signals so that other threads possibly created later (for ex.
96 * in modules) do not receive signals like SIGINT
97 */
98 sigset_t oldmask, newmask;
99 sigfillset(&newmask);
100 ret = pthread_sigmask(SIG_BLOCK,&newmask,&oldmask);
101 WZD_ASSERT( ret == 0 );
102 }
103
104 ret = pthread_create( & thread->_t, & attr->_a, start_routine, arg);
105
106 {
107 /* restore signals so we can be stopped with SIGINT or restarted with SIGHUP */
108 sigset_t oldmask, newmask;
109 sigfillset(&newmask);
110 ret = pthread_sigmask(SIG_UNBLOCK,&newmask,&oldmask);
111 WZD_ASSERT( ret == 0 );
112 }
113
114 return ret;
115 #else
116 unsigned long threadID;
117
118 thread->_t = CreateThread( NULL /* not supported yet */, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0 /* creation flags */, &threadID);
119
120 return (thread->_v == NULL);
121 #endif
122 }
123
124 /** \brief Initialize an empty wzd_thread_attr_t structure
125 *
126 * \param[out] attr pointer to the new attributes structure
127 *
128 * \return 0 if ok
129 */
wzd_thread_attr_init(wzd_thread_attr_t * attr)130 int wzd_thread_attr_init(wzd_thread_attr_t * attr)
131 {
132 #ifndef WIN32
133 return pthread_attr_init( & attr->_a );
134 #else
135 attr->_v = NULL;
136 return 0;
137 #endif
138 }
139
140 /** \brief Free resources used by a wzd_thread_attr_t structure
141 *
142 * \param[in] attr pointer to the attributes structure
143 *
144 * \return 0 if ok
145 */
wzd_thread_attr_destroy(wzd_thread_attr_t * attr)146 int wzd_thread_attr_destroy(wzd_thread_attr_t * attr)
147 {
148 #ifndef WIN32
149 return pthread_attr_destroy( & attr->_a );
150 #else
151 attr->_v = NULL;
152 return 0;
153 #endif
154 }
155
156 /** \brief Set thread attribute to detachable state
157 *
158 * Resources used by a detached thread are freed immediatly when
159 * the thread exits, and wzd_thread_join can't be used to get the
160 * return code.
161 *
162 * \param[in] attr attributes structure
163 *
164 * \return 0 if ok
165 */
wzd_thread_attr_set_detached(wzd_thread_attr_t * attr)166 int wzd_thread_attr_set_detached(wzd_thread_attr_t * attr)
167 {
168 #ifndef WIN32
169 return pthread_attr_setdetachstate( & attr->_a, PTHREAD_CREATE_DETACHED);
170 #else
171 return 0;
172 #endif
173 }
174
175 /** \brief Wait for termination of another thread
176 *
177 * Wait indefinitly, until thread terminates. The return code is stored
178 * in thread_return, and the thread can be freed after.
179 *
180 * \param[in] thread the thread id to wait for
181 * \param[out] thread_return the return value of thread
182 *
183 * \return 0 if ok
184 */
wzd_thread_join(wzd_thread_t * thread,void ** thread_return)185 int wzd_thread_join(wzd_thread_t * thread, void ** thread_return)
186 {
187 #ifndef WIN32
188 return pthread_join(thread->_t, thread_return);
189 #else
190
191 if (WaitForSingleObject(thread->_t, INFINITE) != WAIT_OBJECT_0)
192 {
193 out_log(LEVEL_CRITICAL, "Thread join failed.");
194 CloseHandle(thread->_t);
195
196 return -1;
197 }
198 CloseHandle(thread->_t);
199
200 return 0;
201 #endif
202 }
203
204 /** \brief Cancel thread by sending a signal
205 *
206 * \param[in] thread the thread to cancel
207 */
wzd_thread_cancel(wzd_thread_t * thread)208 int wzd_thread_cancel(wzd_thread_t * thread)
209 {
210 #ifndef WIN32
211 return pthread_cancel(thread->_t);
212 #else
213 /** \todo use pthread_kill() equivalent for windows */
214 out_log(LEVEL_CRITICAL, "Not Yet Implemented : wzd_thread_cancel\n");
215 return -1;
216 #endif
217 }
218
219 /** \brief Allocate a new thread-local storage
220 *
221 * If a TLS is already allocated, do nothing
222 *
223 * \return
224 * - a unique identifier to a thread specific data area (TSD) if ok
225 * - NULL on error
226 */
wzd_tls_allocate()227 struct thread_key_t * wzd_tls_allocate()
228 {
229 struct thread_key_t * thread_key = NULL;
230 thread_key = malloc(sizeof(struct thread_key_t));
231 #ifdef HAVE_PTHREAD
232 {
233 int ret;
234 ret = pthread_key_create(&thread_key->key,NULL);
235 }
236 #else
237 thread_key->key = TlsAlloc();
238 #endif
239 return thread_key;
240 }
241
242 /** \brief Free thread-local storage but not key
243 *
244 * \param[in] thread_key key to TSD
245 * \return 0 if ok
246 */
wzd_tls_remove(struct thread_key_t * thread_key)247 int wzd_tls_remove(struct thread_key_t * thread_key)
248 {
249 int ret;
250 if (thread_key != NULL) {
251 #ifdef HAVE_PTHREAD
252 ret = pthread_key_delete(thread_key->key);
253 #else
254 ret = TlsFree(thread_key->key);
255 #endif
256 }
257 return 0;
258 }
259
260 /** \brief Free thread-local storage and key
261 *
262 * \param[in] thread_key key to TSD
263 * \return 0 if ok
264 */
wzd_tls_free(struct thread_key_t * thread_key)265 int wzd_tls_free(struct thread_key_t * thread_key)
266 {
267 int ret;
268 if (thread_key != NULL) {
269 #ifdef HAVE_PTHREAD
270 ret = pthread_key_delete(thread_key->key);
271 #else
272 ret = TlsFree(thread_key->key);
273 #endif
274 free(thread_key);
275 thread_key = NULL;
276 }
277 return 0;
278 }
279
280 /** \brief Store value in TLS
281 *
282 * \param[in] thread_key key to TSD
283 * \param[in] data_ptr pointer to the data which will be duplicated for the thread
284 * \return 0 if ok
285 */
wzd_tls_setspecific(struct thread_key_t * thread_key,const void * data_ptr)286 int wzd_tls_setspecific(struct thread_key_t * thread_key, const void * data_ptr)
287 {
288 #ifdef HAVE_PTHREAD
289 return pthread_setspecific(thread_key->key,data_ptr);
290 #else
291 return TlsSetValue(thread_key->key,data_ptr);
292 #endif
293 }
294
295 /** \brief Get value from TLS
296 *
297 * \param[in] thread_key key to TSD
298 * \return 0 if ok
299 */
wzd_tls_getspecific(struct thread_key_t * thread_key)300 void * wzd_tls_getspecific(struct thread_key_t * thread_key)
301 {
302 #ifdef HAVE_PTHREAD
303 return pthread_getspecific(thread_key->key);
304 #else
305 return TlsGetValue(thread_key->key);
306 #endif
307 }
308
309