1/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15
16/* Functions to get threads more portable */
17
18#define DONT_REMAP_PTHREAD_FUNCTIONS
19
20#include "mysys_priv.h"
21#include <signal.h>
22#include <m_string.h>
23#include <thr_alarm.h>
24#include <my_pthread.h>
25
26#if (defined(__BSD__) || defined(_BSDI_VERSION))
27#define SCHED_POLICY SCHED_RR
28#else
29#define SCHED_POLICY SCHED_OTHER
30#endif
31
32uint thd_lib_detected= 0;
33
34/*
35 Some functions for RTS threads, AIX, Siemens Unix and UnixWare 7
36 (and DEC OSF/1 3.2 too)
37*/
38
39int my_pthread_create_detached=1;
40
41/* localtime_r for SCO 3.2V4.2 */
42
43#if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
44
45extern mysql_mutex_t LOCK_localtime_r;
46
47#endif
48
49#if !defined(HAVE_LOCALTIME_R)
50struct tm *localtime_r(const time_t *clock, struct tm *res)
51{
52 struct tm *tmp;
53 mysql_mutex_lock(&LOCK_localtime_r);
54 tmp=localtime(clock);
55 *res= *tmp;
56 mysql_mutex_unlock(&LOCK_localtime_r);
57 return res;
58}
59#endif
60
61#if !defined(HAVE_GMTIME_R)
62/*
63 Reentrant version of standard gmtime() function.
64 Needed on some systems which don't implement it.
65*/
66
67struct tm *gmtime_r(const time_t *clock, struct tm *res)
68{
69 struct tm *tmp;
70 mysql_mutex_lock(&LOCK_localtime_r);
71 tmp= gmtime(clock);
72 *res= *tmp;
73 mysql_mutex_unlock(&LOCK_localtime_r);
74 return res;
75}
76#endif
77
78/****************************************************************************
79** Replacement of sigwait if the system doesn't have one (like BSDI 3.0)
80**
81** Note:
82** This version of sigwait() is assumed to called in a loop so the signalmask
83** is permanently modified to reflect the signal set. This is done to get
84** a much faster implementation.
85**
86** This implementation isn't thread safe: It assumes that only one
87** thread is using sigwait.
88**
89** If one later supplies a different signal mask, all old signals that
90** was used before are unblocked and set to SIGDFL.
91**
92** Author: Gary Wisniewski <garyw@spidereye.com.au>, much modified by Monty
93****************************************************************************/
94
95#if !defined(HAVE_SIGWAIT) && !defined(sigwait) && !defined(__WIN__) && !defined(HAVE_rts_threads)
96
97#if !defined(DONT_USE_SIGSUSPEND)
98
99static sigset_t sigwait_set,rev_sigwait_set,px_recd;
100
101void px_handle_sig(int sig)
102{
103 sigaddset(&px_recd, sig);
104}
105
106
107void sigwait_setup(sigset_t *set)
108{
109 int i;
110 struct sigaction sact,sact1;
111 sigset_t unblock_mask;
112
113 sact.sa_flags = 0;
114 sact.sa_handler = px_handle_sig;
115 memcpy(&sact.sa_mask, set, sizeof(*set)); /* handler isn't thread_safe */
116 sigemptyset(&unblock_mask);
117 pthread_sigmask(SIG_UNBLOCK,(sigset_t*) 0,&rev_sigwait_set);
118
119 for (i = 1; i <= sizeof(sigwait_set)*8; i++)
120 {
121 if (sigismember(set,i))
122 {
123 sigdelset(&rev_sigwait_set,i);
124 if (!sigismember(&sigwait_set,i))
125 sigaction(i, &sact, (struct sigaction*) 0);
126 }
127 else
128 {
129 sigdelset(&px_recd,i); /* Don't handle this */
130 if (sigismember(&sigwait_set,i))
131 { /* Remove the old handler */
132 sigaddset(&unblock_mask,i);
133 sigdelset(&rev_sigwait_set,i);
134 sact1.sa_flags = 0;
135 sact1.sa_handler = SIG_DFL;
136 sigemptyset(&sact1.sa_mask);
137 sigaction(i, &sact1, 0);
138 }
139 }
140 }
141 memcpy(&sigwait_set, set, sizeof(*set));
142 pthread_sigmask(SIG_BLOCK,(sigset_t*) set,(sigset_t*) 0);
143 pthread_sigmask(SIG_UNBLOCK,&unblock_mask,(sigset_t*) 0);
144}
145
146
147int sigwait(sigset_t *setp, int *sigp)
148{
149 if (memcmp(setp,&sigwait_set,sizeof(sigwait_set)))
150 sigwait_setup(setp); /* Init or change of set */
151
152 for (;;)
153 {
154 /*
155 This is a fast, not 100% portable implementation to find the signal.
156 Because the handler is blocked there should be at most 1 bit set, but
157 the specification on this is somewhat shady so we use a set instead a
158 single variable.
159 */
160
161 ulong *ptr= (ulong*) &px_recd;
162 ulong *end=ptr+sizeof(px_recd)/sizeof(ulong);
163
164 for ( ; ptr != end ; ptr++)
165 {
166 if (*ptr)
167 {
168 ulong set= *ptr;
169 int found= (int) ((char*) ptr - (char*) &px_recd)*8+1;
170 while (!(set & 1))
171 {
172 found++;
173 set>>=1;
174 }
175 *sigp=found;
176 sigdelset(&px_recd,found);
177 return 0;
178 }
179 }
180 sigsuspend(&rev_sigwait_set);
181 }
182 return 0;
183}
184#else /* !DONT_USE_SIGSUSPEND */
185
186/****************************************************************************
187** Replacement of sigwait if the system doesn't have one (like BSDI 3.0)
188**
189** Note:
190** This version of sigwait() is assumed to called in a loop so the signalmask
191** is permanently modified to reflect the signal set. This is done to get
192** a much faster implementation.
193**
194** This implementation uses a extra thread to handle the signals and one
195** must always call sigwait() with the same signal mask!
196**
197** BSDI 3.0 NOTE:
198**
199** pthread_kill() doesn't work on a thread in a select() or sleep() loop?
200** After adding the sleep to sigwait_thread, all signals are checked and
201** delivered every second. This isn't that terrible performance vice, but
202** someone should report this to BSDI and ask for a fix!
203** Another problem is that when the sleep() ends, every select() in other
204** threads are interrupted!
205****************************************************************************/
206
207static sigset_t pending_set;
208static bool inited=0;
209static pthread_cond_t COND_sigwait;
210static pthread_mutex_t LOCK_sigwait;
211
212
213void sigwait_handle_sig(int sig)
214{
215 pthread_mutex_lock(&LOCK_sigwait);
216 sigaddset(&pending_set, sig);
217 pthread_cond_signal(&COND_sigwait); /* inform sigwait() about signal */
218 pthread_mutex_unlock(&LOCK_sigwait);
219}
220
221void *sigwait_thread(void *set_arg)
222{
223 sigset_t *set=(sigset_t*) set_arg;
224
225 int i;
226 struct sigaction sact;
227 sact.sa_flags = 0;
228 sact.sa_handler = sigwait_handle_sig;
229 memcpy(&sact.sa_mask, set, sizeof(*set)); /* handler isn't thread_safe */
230 sigemptyset(&pending_set);
231
232 for (i = 1; i <= sizeof(pending_set)*8; i++)
233 {
234 if (sigismember(set,i))
235 {
236 sigaction(i, &sact, (struct sigaction*) 0);
237 }
238 }
239 /* Ensure that init_thr_alarm() is called */
240 DBUG_ASSERT(thr_client_alarm);
241 sigaddset(set, thr_client_alarm);
242 pthread_sigmask(SIG_UNBLOCK,(sigset_t*) set,(sigset_t*) 0);
243 alarm_thread=pthread_self(); /* For thr_alarm */
244
245 for (;;)
246 { /* Wait for signals */
247 sleep(1); /* Because of broken BSDI */
248 }
249}
250
251
252int sigwait(sigset_t *setp, int *sigp)
253{
254 if (!inited)
255 {
256 pthread_attr_t thr_attr;
257 pthread_t sigwait_thread_id;
258 inited=1;
259 sigemptyset(&pending_set);
260 pthread_mutex_init(&LOCK_sigwait, MY_MUTEX_INIT_FAST);
261 pthread_cond_init(&COND_sigwait, NULL);
262
263 pthread_attr_init(&thr_attr);
264 pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
265 pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
266 pthread_attr_setstacksize(&thr_attr,8196);
267 pthread_create(&sigwait_thread_id, &thr_attr, sigwait_thread, setp);
268 pthread_attr_destroy(&thr_attr);
269 }
270
271 pthread_mutex_lock(&LOCK_sigwait);
272 for (;;)
273 {
274 ulong *ptr= (ulong*) &pending_set;
275 ulong *end=ptr+sizeof(pending_set)/sizeof(ulong);
276
277 for ( ; ptr != end ; ptr++)
278 {
279 if (*ptr)
280 {
281 ulong set= *ptr;
282 int found= (int) ((char*) ptr - (char*) &pending_set)*8+1;
283 while (!(set & 1))
284 {
285 found++;
286 set>>=1;
287 }
288 *sigp=found;
289 sigdelset(&pending_set,found);
290 pthread_mutex_unlock(&LOCK_sigwait);
291 return 0;
292 }
293 }
294 pthread_cond_wait(&COND_sigwait, &LOCK_sigwait);
295 }
296 return 0;
297}
298
299#endif /* DONT_USE_SIGSUSPEND */
300#endif /* HAVE_SIGWAIT */
301
302
303/****************************************************************************
304 The following functions fixes that all pthread functions should work
305 according to latest posix standard
306****************************************************************************/
307
308/* Undefined wrappers set my_pthread.h so that we call os functions */
309#undef pthread_mutex_init
310#undef pthread_mutex_lock
311#undef pthread_mutex_unlock
312#undef pthread_mutex_destroy
313#undef pthread_mutex_wait
314#undef pthread_mutex_timedwait
315#undef pthread_mutex_trylock
316#undef pthread_mutex_t
317#undef pthread_cond_init
318#undef pthread_cond_wait
319#undef pthread_cond_timedwait
320#undef pthread_cond_t
321#undef pthread_attr_getstacksize
322
323/*****************************************************************************
324 Patches for HPUX
325 We need these because the pthread_mutex.. code returns -1 on error,
326 instead of the error code.
327
328 Note that currently we only remap pthread_ functions used by MySQL.
329 If we are depending on the value for some other pthread_xxx functions,
330 this has to be added here.
331****************************************************************************/
332
333#if defined(HPUX10)
334
335int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
336 struct timespec *abstime)
337{
338 int error=pthread_cond_timedwait(cond, mutex, abstime);
339 if (error == -1) /* Safety if the lib is fixed */
340 {
341 if (!(error=errno))
342 error= ETIMEDOUT; /* Can happen on HPUX */
343 }
344 if (error == EAGAIN) /* Correct errno to Posix */
345 error= ETIMEDOUT;
346 return error;
347}
348#endif
349
350#if defined(HPUX10)
351
352void my_pthread_attr_getstacksize(pthread_attr_t *connection_attrib,
353 size_t *stack_size)
354{
355 *stack_size= pthread_attr_getstacksize(*connection_attrib);
356}
357#endif
358
359
360#ifdef HAVE_POSIX1003_4a_MUTEX
361/*
362 In HP-UX-10.20 and other old Posix 1003.4a Draft 4 implementations
363 pthread_mutex_trylock returns 1 on success, not 0 like
364 pthread_mutex_lock
365
366 From the HP-UX-10.20 man page:
367 RETURN VALUES
368 If the function fails, errno may be set to one of the following
369 values:
370 Return | Error | Description
371 _______|__________|_________________________________________
372 1 | | Successful completion.
373 0 | | The mutex is locked; therefore, it was
374 | | not acquired.
375 -1 | [EINVAL] | The value specified by mutex is invalid.
376
377*/
378
379/*
380 Convert pthread_mutex_trylock to return values according to latest POSIX
381
382 RETURN VALUES
383 0 If we are able successfully lock the mutex.
384 EBUSY Mutex was locked by another thread
385 # Other error number returned by pthread_mutex_trylock()
386 (Not likely)
387*/
388
389int my_pthread_mutex_trylock(pthread_mutex_t *mutex)
390{
391 int error= pthread_mutex_trylock(mutex);
392 if (error == 1)
393 return 0; /* Got lock on mutex */
394 if (error == 0) /* Someon else is locking mutex */
395 return EBUSY;
396 if (error == -1) /* Safety if the lib is fixed */
397 error= errno; /* Probably invalid parameter */
398 return error;
399}
400#endif /* HAVE_POSIX1003_4a_MUTEX */
401
402/* Some help functions */
403
404int pthread_dummy(int ret)
405{
406 return ret;
407}
408