1/* Copyright (c) 2000, 2013, Oracle and/or its affiliates
2 Copyright (c) 2012, 2014, SkySQL Ab
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17/* To avoid problems with alarms in debug code, we disable DBUG here */
18#define FORCE_DBUG_OFF
19#include "mysys_priv.h"
20#include <my_global.h>
21
22#if !defined(DONT_USE_THR_ALARM)
23#include <errno.h>
24#include <my_pthread.h>
25#include <signal.h>
26#include <my_sys.h>
27#include <m_string.h>
28#include <queues.h>
29#include "thr_alarm.h"
30
31#ifdef HAVE_SYS_SELECT_H
32#include <sys/select.h> /* AIX needs this for fd_set */
33#endif
34
35#ifndef ETIME
36#define ETIME ETIMEDOUT
37#endif
38
39#ifdef DBUG_OFF
40#define reset_index_in_queue(alarm_data)
41#else
42#define reset_index_in_queue(alarm_data) alarm_data->index_in_queue= 0;
43#endif /* DBUG_OFF */
44
45#ifndef USE_ONE_SIGNAL_HAND
46#define one_signal_hand_sigmask(A,B,C) pthread_sigmask((A), (B), (C))
47#else
48#define one_signal_hand_sigmask(A,B,C)
49#endif
50
51my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0;
52
53#if !defined(__WIN__)
54
55uint thr_client_alarm;
56static int alarm_aborted=1; /* No alarm thread */
57volatile my_bool alarm_thread_running= 0;
58time_t next_alarm_expire_time= ~ (time_t) 0;
59static sig_handler process_alarm_part2(int sig);
60
61static mysql_mutex_t LOCK_alarm;
62static mysql_cond_t COND_alarm;
63static sigset_t full_signal_set;
64static QUEUE alarm_queue;
65static uint max_used_alarms=0;
66pthread_t alarm_thread;
67
68#define MY_THR_ALARM_QUEUE_EXTENT 10
69
70#ifdef USE_ALARM_THREAD
71static void *alarm_handler(void *arg);
72#define reschedule_alarms() mysql_cond_signal(&COND_alarm)
73#else
74#define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM)
75#endif
76
77static sig_handler thread_alarm(int sig __attribute__((unused)));
78
79static int compare_ulong(void *not_used __attribute__((unused)),
80 uchar *a_ptr,uchar* b_ptr)
81{
82 ulong a=*((ulong*) a_ptr),b= *((ulong*) b_ptr);
83 return (a < b) ? -1 : (a == b) ? 0 : 1;
84}
85
86void init_thr_alarm(uint max_alarms)
87{
88 sigset_t s;
89 DBUG_ENTER("init_thr_alarm");
90 alarm_aborted=0;
91 next_alarm_expire_time= ~ (time_t) 0;
92 init_queue(&alarm_queue, max_alarms+1, offsetof(ALARM,expire_time), 0,
93 compare_ulong, NullS, offsetof(ALARM, index_in_queue)+1,
94 MY_THR_ALARM_QUEUE_EXTENT);
95 sigfillset(&full_signal_set); /* Neaded to block signals */
96 mysql_mutex_init(key_LOCK_alarm, &LOCK_alarm, MY_MUTEX_INIT_FAST);
97 mysql_cond_init(key_COND_alarm, &COND_alarm, NULL);
98 if (thd_lib_detected == THD_LIB_LT)
99 thr_client_alarm= SIGALRM;
100 else
101 thr_client_alarm= SIGUSR1;
102#ifndef USE_ALARM_THREAD
103 if (thd_lib_detected != THD_LIB_LT)
104#endif
105 {
106 my_sigset(thr_client_alarm, thread_alarm);
107 }
108 sigemptyset(&s);
109 sigaddset(&s, THR_SERVER_ALARM);
110 alarm_thread=pthread_self();
111#if defined(USE_ALARM_THREAD)
112 {
113 pthread_attr_t thr_attr;
114 pthread_attr_init(&thr_attr);
115 pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
116 pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
117 pthread_attr_setstacksize(&thr_attr,8196);
118 mysql_thread_create(key_thread_alarm,
119 &alarm_thread, &thr_attr, alarm_handler, NULL);
120 pthread_attr_destroy(&thr_attr);
121 }
122#elif defined(USE_ONE_SIGNAL_HAND)
123 pthread_sigmask(SIG_BLOCK, &s, NULL); /* used with sigwait() */
124 if (thd_lib_detected == THD_LIB_LT)
125 {
126 my_sigset(thr_client_alarm, process_alarm); /* Linuxthreads */
127 pthread_sigmask(SIG_UNBLOCK, &s, NULL);
128 }
129#else
130 my_sigset(THR_SERVER_ALARM, process_alarm);
131 pthread_sigmask(SIG_UNBLOCK, &s, NULL);
132#endif
133 DBUG_VOID_RETURN;
134}
135
136
137void resize_thr_alarm(uint max_alarms)
138{
139 mysql_mutex_lock(&LOCK_alarm);
140 /*
141 It's ok not to shrink the queue as there may be more pending alarms than
142 than max_alarms
143 */
144 if (alarm_queue.elements < max_alarms)
145 {
146 resize_queue(&alarm_queue,max_alarms+1);
147 max_used_alarms= alarm_queue.elements;
148 }
149 mysql_mutex_unlock(&LOCK_alarm);
150}
151
152
153/*
154 Request alarm after sec seconds.
155
156 SYNOPSIS
157 thr_alarm()
158 alrm Pointer to alarm detection
159 alarm_data Structure to store in alarm queue
160
161 NOTES
162 This function can't be called from the alarm-handling thread.
163
164 RETURN VALUES
165 0 ok
166 1 If no more alarms are allowed (aborted by process)
167
168 Stores in first argument a pointer to a non-zero int which is set to 0
169 when the alarm has been given
170*/
171
172my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
173{
174 time_t now, next;
175#ifndef USE_ONE_SIGNAL_HAND
176 sigset_t old_mask;
177#endif
178 my_bool reschedule;
179 struct st_my_thread_var *current_my_thread_var= my_thread_var;
180 DBUG_ENTER("thr_alarm");
181 DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec));
182
183 if (my_disable_thr_alarm)
184 {
185 (*alrm)= &alarm_data->alarmed;
186 alarm_data->alarmed= 1; /* Abort if interrupted */
187 DBUG_RETURN(0);
188 }
189
190 if (unlikely(alarm_aborted))
191 { /* No signal thread */
192 DBUG_PRINT("info", ("alarm aborted"));
193 if (alarm_aborted > 0)
194 goto abort_no_unlock;
195 sec= 1; /* Abort mode */
196 }
197
198 now= my_time(0);
199 if (!alarm_data)
200 {
201 if (!(alarm_data=(ALARM*) my_malloc(sizeof(ALARM),MYF(MY_WME))))
202 goto abort_no_unlock;
203 alarm_data->malloced= 1;
204 }
205 else
206 alarm_data->malloced= 0;
207 next= now + sec;
208 alarm_data->expire_time= next;
209 alarm_data->alarmed= 0;
210 alarm_data->thread= current_my_thread_var->pthread_self;
211 alarm_data->thread_id= current_my_thread_var->id;
212
213 one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
214 mysql_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */
215 if (alarm_queue.elements >= max_used_alarms)
216 {
217 max_used_alarms=alarm_queue.elements+1;
218 }
219 reschedule= (ulong) next_alarm_expire_time > (ulong) next;
220 queue_insert_safe(&alarm_queue, (uchar*) alarm_data);
221 assert(alarm_data->index_in_queue > 0);
222
223 /* Reschedule alarm if the current one has more than sec left */
224 if (unlikely(reschedule))
225 {
226 DBUG_PRINT("info", ("reschedule"));
227 if (pthread_equal(pthread_self(),alarm_thread))
228 {
229 alarm(sec); /* purecov: inspected */
230 next_alarm_expire_time= next;
231 }
232 else
233 reschedule_alarms(); /* Reschedule alarms */
234 }
235 mysql_mutex_unlock(&LOCK_alarm);
236 one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL);
237 (*alrm)= &alarm_data->alarmed;
238 DBUG_RETURN(0);
239
240abort_no_unlock:
241 *alrm= 0; /* No alarm */
242 DBUG_RETURN(1);
243}
244
245
246/*
247 Remove alarm from list of alarms
248*/
249
250void thr_end_alarm(thr_alarm_t *alarmed)
251{
252 ALARM *alarm_data;
253#ifndef USE_ONE_SIGNAL_HAND
254 sigset_t old_mask;
255#endif
256 DBUG_ENTER("thr_end_alarm");
257
258 if (my_disable_thr_alarm)
259 DBUG_VOID_RETURN;
260 one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
261 alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed));
262 mysql_mutex_lock(&LOCK_alarm);
263 DBUG_ASSERT(alarm_data->index_in_queue != 0);
264 DBUG_ASSERT((ALARM*) queue_element(&alarm_queue,
265 alarm_data->index_in_queue) ==
266 alarm_data);
267 queue_remove(&alarm_queue, alarm_data->index_in_queue);
268 mysql_mutex_unlock(&LOCK_alarm);
269 one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL);
270 reset_index_in_queue(alarm_data);
271 DBUG_VOID_RETURN;
272}
273
274/*
275 Come here when some alarm in queue is due.
276 Mark all alarms with are finnished in list.
277 Schedule alarms to be sent again after 1-10 sec (many alarms at once)
278 If alarm_aborted is set then all alarms are given and resent
279 every second.
280*/
281
282sig_handler process_alarm(int sig __attribute__((unused)))
283{
284 sigset_t old_mask;
285/*
286 This must be first as we can't call DBUG inside an alarm for a normal thread
287*/
288
289 if (thd_lib_detected == THD_LIB_LT &&
290 !pthread_equal(pthread_self(),alarm_thread))
291 {
292#if defined(MAIN) && !defined(__bsdi__)
293 printf("thread_alarm in process_alarm\n"); fflush(stdout);
294#endif
295#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
296 my_sigset(thr_client_alarm, process_alarm); /* int. thread system calls */
297#endif
298 return;
299 }
300
301 /*
302 We have to do do the handling of the alarm in a sub function,
303 because otherwise we would get problems with two threads calling
304 DBUG_... functions at the same time (as two threads may call
305 process_alarm() at the same time
306 */
307
308#ifndef USE_ALARM_THREAD
309 pthread_sigmask(SIG_SETMASK,&full_signal_set,&old_mask);
310 mysql_mutex_lock(&LOCK_alarm);
311#endif
312 process_alarm_part2(sig);
313#ifndef USE_ALARM_THREAD
314#if defined(SIGNAL_HANDLER_RESET_ON_DELIVERY) && !defined(USE_ONE_SIGNAL_HAND)
315 my_sigset(THR_SERVER_ALARM,process_alarm);
316#endif
317 mysql_mutex_unlock(&LOCK_alarm);
318 pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
319#endif
320 return;
321}
322
323
324static sig_handler process_alarm_part2(int sig __attribute__((unused)))
325{
326 ALARM *alarm_data;
327 DBUG_ENTER("process_alarm");
328 DBUG_PRINT("info",("sig: %d active alarms: %d",sig,alarm_queue.elements));
329
330#if defined(MAIN) && !defined(__bsdi__)
331 printf("process_alarm\n"); fflush(stdout);
332#endif
333 if (likely(alarm_queue.elements))
334 {
335 if (unlikely(alarm_aborted))
336 {
337 uint i;
338 for (i= queue_first_element(&alarm_queue) ;
339 i <= queue_last_element(&alarm_queue) ;)
340 {
341 alarm_data=(ALARM*) queue_element(&alarm_queue,i);
342 alarm_data->alarmed=1; /* Info to thread */
343 if (pthread_equal(alarm_data->thread,alarm_thread) ||
344 pthread_kill(alarm_data->thread, thr_client_alarm))
345 {
346#ifdef MAIN
347 printf("Warning: pthread_kill couldn't find thread!!!\n");
348#endif
349 queue_remove(&alarm_queue,i); /* No thread. Remove alarm */
350 reset_index_in_queue(alarm_data);
351 }
352 else
353 i++; /* Signal next thread */
354 }
355#ifndef USE_ALARM_THREAD
356 if (alarm_queue.elements)
357 alarm(1); /* Signal soon again */
358#endif
359 }
360 else
361 {
362 time_t now= my_time(0);
363 time_t next= now+10-(now%10);
364 while ((alarm_data=(ALARM*) queue_top(&alarm_queue))->expire_time <= now)
365 {
366 alarm_data->alarmed=1; /* Info to thread */
367 DBUG_PRINT("info",("sending signal to waiting thread"));
368 if (pthread_equal(alarm_data->thread,alarm_thread) ||
369 pthread_kill(alarm_data->thread, thr_client_alarm))
370 {
371#ifdef MAIN
372 printf("Warning: pthread_kill couldn't find thread!!!\n");
373#endif /* MAIN */
374 queue_remove_top(&alarm_queue); /* No thread. Remove alarm */
375 reset_index_in_queue(alarm_data);
376 if (!alarm_queue.elements)
377 break;
378 }
379 else
380 {
381 alarm_data->expire_time=next;
382 queue_replace_top(&alarm_queue);
383 }
384 }
385#ifndef USE_ALARM_THREAD
386 if (alarm_queue.elements)
387 {
388#ifdef __bsdi__
389 alarm(0); /* Remove old alarm */
390#endif
391 alarm((uint) (alarm_data->expire_time-now));
392 next_alarm_expire_time= alarm_data->expire_time;
393 }
394#endif
395 }
396 }
397 else
398 {
399 /*
400 Ensure that next time we call thr_alarm(), we will schedule a new alarm
401 */
402 next_alarm_expire_time= ~(time_t) 0;
403 }
404 DBUG_VOID_RETURN;
405}
406
407
408/*
409 Schedule all alarms now and optionally free all structures
410
411 SYNPOSIS
412 end_thr_alarm()
413 free_structures Set to 1 if we should free memory used for
414 the alarm queue.
415 When we call this we should KNOW that there
416 is no active alarms
417 IMPLEMENTATION
418 Set alarm_abort to -1 which will change the behavior of alarms as follows:
419 - All old alarms will be rescheduled at once
420 - All new alarms will be rescheduled to one second
421*/
422
423void end_thr_alarm(my_bool free_structures)
424{
425 DBUG_ENTER("end_thr_alarm");
426 if (alarm_aborted != 1) /* If memory not freed */
427 {
428 mysql_mutex_lock(&LOCK_alarm);
429 DBUG_PRINT("info",("Rescheduling %d waiting alarms",alarm_queue.elements));
430 alarm_aborted= -1; /* mark aborted */
431 if (alarm_queue.elements || (alarm_thread_running && free_structures))
432 {
433 if (pthread_equal(pthread_self(),alarm_thread))
434 alarm(1); /* Shut down everything soon */
435 else
436 reschedule_alarms();
437 }
438 if (free_structures)
439 {
440 struct timespec abstime;
441
442 DBUG_ASSERT(!alarm_queue.elements);
443
444 /* Wait until alarm thread dies */
445 set_timespec(abstime, 10); /* Wait up to 10 seconds */
446 while (alarm_thread_running)
447 {
448 int error= mysql_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime);
449 if (error == ETIME || error == ETIMEDOUT)
450 break; /* Don't wait forever */
451 }
452 delete_queue(&alarm_queue);
453 alarm_aborted= 1;
454 mysql_mutex_unlock(&LOCK_alarm);
455 if (!alarm_thread_running) /* Safety */
456 {
457 mysql_mutex_destroy(&LOCK_alarm);
458 mysql_cond_destroy(&COND_alarm);
459 }
460 }
461 else
462 mysql_mutex_unlock(&LOCK_alarm);
463 }
464 DBUG_VOID_RETURN;
465}
466
467
468/*
469 Remove another thread from the alarm
470*/
471
472void thr_alarm_kill(my_thread_id thread_id)
473{
474 uint i;
475 DBUG_ENTER("thr_alarm_kill");
476
477 if (alarm_aborted)
478 return;
479 mysql_mutex_lock(&LOCK_alarm);
480 for (i= queue_first_element(&alarm_queue) ;
481 i <= queue_last_element(&alarm_queue);
482 i++)
483 {
484 ALARM *element= (ALARM*) queue_element(&alarm_queue,i);
485 if (element->thread_id == thread_id)
486 {
487 DBUG_PRINT("info", ("found thread; Killing it"));
488 element->expire_time= 0;
489 queue_replace(&alarm_queue, i);
490 reschedule_alarms();
491 break;
492 }
493 }
494 mysql_mutex_unlock(&LOCK_alarm);
495 DBUG_VOID_RETURN;
496}
497
498
499void thr_alarm_info(ALARM_INFO *info)
500{
501 mysql_mutex_lock(&LOCK_alarm);
502 info->next_alarm_time= 0;
503 info->max_used_alarms= max_used_alarms;
504 if ((info->active_alarms= alarm_queue.elements))
505 {
506 time_t now= my_time(0);
507 long time_diff;
508 ALARM *alarm_data= (ALARM*) queue_top(&alarm_queue);
509 time_diff= (long) (alarm_data->expire_time - now);
510 info->next_alarm_time= (ulong) (time_diff < 0 ? 0 : time_diff);
511 }
512 mysql_mutex_unlock(&LOCK_alarm);
513}
514
515/*
516 This is here for thread to get interruptet from read/write/fcntl
517 ARGSUSED
518*/
519
520
521static sig_handler thread_alarm(int sig __attribute__((unused)))
522{
523#ifdef MAIN
524 printf("thread_alarm\n"); fflush(stdout);
525#endif
526#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
527 my_sigset(sig,thread_alarm); /* int. thread system calls */
528#endif
529}
530
531
532#ifdef HAVE_TIMESPEC_TS_SEC
533#define tv_sec ts_sec
534#define tv_nsec ts_nsec
535#endif
536
537/* set up a alarm thread with uses 'sleep' to sleep between alarms */
538
539#ifdef USE_ALARM_THREAD
540static void *alarm_handler(void *arg __attribute__((unused)))
541{
542 int error;
543 struct timespec abstime;
544#ifdef MAIN
545 puts("Starting alarm thread");
546#endif
547 my_thread_init();
548 alarm_thread_running= 1;
549 mysql_mutex_lock(&LOCK_alarm);
550 for (;;)
551 {
552 if (alarm_queue.elements)
553 {
554 time_t sleep_time,now= my_time(0);
555 if (alarm_aborted)
556 sleep_time=now+1;
557 else
558 sleep_time= ((ALARM*) queue_top(&alarm_queue))->expire_time;
559 if (sleep_time > now)
560 {
561 abstime.tv_sec=sleep_time;
562 abstime.tv_nsec=0;
563 next_alarm_expire_time= sleep_time;
564 if ((error= mysql_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime)) &&
565 error != ETIME && error != ETIMEDOUT)
566 {
567#ifdef MAIN
568 printf("Got error: %d from ptread_cond_timedwait (errno: %d)\n",
569 error,errno);
570#endif
571 }
572 }
573 }
574 else if (alarm_aborted == -1)
575 break;
576 else
577 {
578 next_alarm_expire_time= ~ (time_t) 0;
579 if ((error= mysql_cond_wait(&COND_alarm, &LOCK_alarm)))
580 {
581#ifdef MAIN
582 printf("Got error: %d from ptread_cond_wait (errno: %d)\n",
583 error,errno);
584#endif
585 }
586 }
587 process_alarm(0);
588 }
589 bzero((char*) &alarm_thread,sizeof(alarm_thread)); /* For easy debugging */
590 alarm_thread_running= 0;
591 mysql_cond_signal(&COND_alarm);
592 mysql_mutex_unlock(&LOCK_alarm);
593 pthread_exit(0);
594 return 0; /* Impossible */
595}
596#endif /* USE_ALARM_THREAD */
597#endif
598
599/****************************************************************************
600 Handling of test case (when compiled with -DMAIN)
601***************************************************************************/
602
603#ifdef MAIN
604#if !defined(DONT_USE_THR_ALARM)
605
606static mysql_cond_t COND_thread_count;
607static mysql_mutex_t LOCK_thread_count;
608static uint thread_count;
609
610#ifdef HPUX10
611typedef int * fd_set_ptr;
612#else
613typedef fd_set * fd_set_ptr;
614#endif /* HPUX10 */
615
616static void *test_thread(void *arg)
617{
618 int i,param=*((int*) arg),wait_time,retry;
619 time_t start_time;
620 thr_alarm_t got_alarm;
621 fd_set fd;
622 FD_ZERO(&fd);
623 my_thread_init();
624 printf("Thread %d (%s) started\n",param,my_thread_name()); fflush(stdout);
625 for (i=1 ; i <= 10 ; i++)
626 {
627 wait_time=param ? 11-i : i;
628 start_time= my_time(0);
629 if (thr_alarm(&got_alarm,wait_time,0))
630 {
631 printf("Thread: %s Alarms aborted\n",my_thread_name());
632 break;
633 }
634 if (wait_time == 3)
635 {
636 printf("Thread: %s Simulation of no alarm needed\n",my_thread_name());
637 fflush(stdout);
638 }
639 else
640 {
641 for (retry=0 ; !thr_got_alarm(&got_alarm) && retry < 10 ; retry++)
642 {
643 printf("Thread: %s Waiting %d sec\n",my_thread_name(),wait_time);
644 select(0,(fd_set_ptr) &fd,0,0,0);
645 }
646 if (!thr_got_alarm(&got_alarm))
647 {
648 printf("Thread: %s didn't get an alarm. Aborting!\n",
649 my_thread_name());
650 break;
651 }
652 if (wait_time == 7)
653 { /* Simulate alarm-miss */
654 fd_set readFDs;
655 uint max_connection=fileno(stdin);
656 FD_ZERO(&readFDs);
657 FD_SET(max_connection,&readFDs);
658 retry=0;
659 for (;;)
660 {
661 printf("Thread: %s Simulating alarm miss\n",my_thread_name());
662 fflush(stdout);
663 if (select(max_connection+1, (fd_set_ptr) &readFDs,0,0,0) < 0)
664 {
665 if (errno == EINTR)
666 break; /* Got new interrupt */
667 printf("Got errno: %d from select. Retrying..\n",errno);
668 if (retry++ >= 3)
669 {
670 printf("Warning: Interrupt of select() doesn't set errno!\n");
671 break;
672 }
673 }
674 else /* This shouldn't happen */
675 {
676 if (!FD_ISSET(max_connection,&readFDs))
677 {
678 printf("Select interrupted, but errno not set\n");
679 fflush(stdout);
680 if (retry++ >= 3)
681 break;
682 continue;
683 }
684 (void) getchar(); /* Somebody was playing */
685 }
686 }
687 }
688 }
689 printf("Thread: %s Slept for %d (%d) sec\n",my_thread_name(),
690 (int) (my_time(0)-start_time), wait_time); fflush(stdout);
691 thr_end_alarm(&got_alarm);
692 fflush(stdout);
693 }
694 mysql_mutex_lock(&LOCK_thread_count);
695 thread_count--;
696 mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
697 mysql_mutex_unlock(&LOCK_thread_count);
698 my_thread_end();
699 return 0;
700}
701
702
703static void *signal_hand(void *arg __attribute__((unused)))
704{
705 sigset_t set;
706 int sig,error,err_count=0;;
707
708 my_thread_init();
709 pthread_detach_this_thread();
710 init_thr_alarm(10); /* Setup alarm handler */
711 mysql_mutex_lock(&LOCK_thread_count); /* Required by bsdi */
712 mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
713 mysql_mutex_unlock(&LOCK_thread_count);
714
715 sigemptyset(&set); /* Catch all signals */
716 sigaddset(&set,SIGINT);
717 sigaddset(&set,SIGQUIT);
718 sigaddset(&set,SIGTERM);
719 sigaddset(&set,SIGHUP);
720#ifdef SIGTSTP
721 sigaddset(&set,SIGTSTP);
722#endif
723#ifdef USE_ONE_SIGNAL_HAND
724 sigaddset(&set,THR_SERVER_ALARM); /* For alarms */
725 puts("Starting signal and alarm handling thread");
726#else
727 puts("Starting signal handling thread");
728#endif
729 printf("server alarm: %d thread alarm: %d\n",
730 THR_SERVER_ALARM, thr_client_alarm);
731 DBUG_PRINT("info",("Starting signal and alarm handling thread"));
732 for(;;)
733 {
734 while ((error=my_sigwait(&set,&sig)) == EINTR)
735 printf("sigwait restarted\n");
736 if (error)
737 {
738 fprintf(stderr,"Got error %d from sigwait\n",error);
739 if (err_count++ > 5)
740 exit(1); /* Too many errors in test */
741 continue;
742 }
743#ifdef USE_ONE_SIGNAL_HAND
744 if (sig != THR_SERVER_ALARM)
745#endif
746 printf("Main thread: Got signal %d\n",sig);
747 switch (sig) {
748 case SIGINT:
749 case SIGQUIT:
750 case SIGTERM:
751 case SIGHUP:
752 printf("Aborting nicely\n");
753 end_thr_alarm(0);
754 break;
755#ifdef SIGTSTP
756 case SIGTSTP:
757 printf("Aborting\n");
758 exit(1);
759 return 0; /* Keep some compilers happy */
760#endif
761#ifdef USE_ONE_SIGNAL_HAND
762 case THR_SERVER_ALARM:
763 process_alarm(sig);
764 break;
765#endif
766 }
767 }
768}
769
770
771int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
772{
773 pthread_t tid;
774 pthread_attr_t thr_attr;
775 int i, param[2], error;
776 sigset_t set;
777 ALARM_INFO alarm_info;
778 MY_INIT(argv[0]);
779
780 if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
781 {
782 DBUG_PUSH(argv[1]+2);
783 }
784 mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
785 mysql_cond_init(0, &COND_thread_count, NULL);
786
787 /* Start a alarm handling thread */
788 sigemptyset(&set);
789 sigaddset(&set,SIGINT);
790 sigaddset(&set,SIGQUIT);
791 sigaddset(&set,SIGTERM);
792 sigaddset(&set,SIGHUP);
793 signal(SIGTERM,SIG_DFL); /* If it's blocked by parent */
794#ifdef SIGTSTP
795 sigaddset(&set,SIGTSTP);
796#endif
797 sigaddset(&set,THR_SERVER_ALARM);
798 sigdelset(&set, thr_client_alarm);
799 (void) pthread_sigmask(SIG_SETMASK,&set,NULL);
800
801 pthread_attr_init(&thr_attr);
802 pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
803 pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
804 pthread_attr_setstacksize(&thr_attr,65536L);
805
806 /* Start signal thread and wait for it to start */
807 mysql_mutex_lock(&LOCK_thread_count);
808 mysql_thread_create(0,
809 &tid, &thr_attr, signal_hand, NULL);
810 mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
811 mysql_mutex_unlock(&LOCK_thread_count);
812 DBUG_PRINT("info",("signal thread created"));
813
814 thr_setconcurrency(3);
815 pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
816 printf("Main thread: %s\n",my_thread_name());
817 for (i=0 ; i < 2 ; i++)
818 {
819 param[i]= i;
820 mysql_mutex_lock(&LOCK_thread_count);
821 if ((error= mysql_thread_create(0,
822 &tid, &thr_attr, test_thread,
823 (void*) &param[i])))
824 {
825 printf("Can't create thread %d, error: %d\n",i,error);
826 exit(1);
827 }
828 thread_count++;
829 mysql_mutex_unlock(&LOCK_thread_count);
830 }
831
832 pthread_attr_destroy(&thr_attr);
833 mysql_mutex_lock(&LOCK_thread_count);
834 thr_alarm_info(&alarm_info);
835 printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n",
836 alarm_info.active_alarms, alarm_info.max_used_alarms,
837 alarm_info.next_alarm_time);
838 while (thread_count)
839 {
840 mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
841 if (thread_count == 1)
842 {
843 printf("Calling end_thr_alarm. This should cancel the last thread\n");
844 end_thr_alarm(0);
845 }
846 }
847 mysql_mutex_unlock(&LOCK_thread_count);
848 thr_alarm_info(&alarm_info);
849 end_thr_alarm(1);
850 printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n",
851 alarm_info.active_alarms, alarm_info.max_used_alarms,
852 alarm_info.next_alarm_time);
853 printf("Test succeeded\n");
854 mysql_cond_destroy(&COND_thread_count);
855 mysql_mutex_destroy(&LOCK_thread_count);
856 my_end(MY_CHECK_ERROR);
857 return 0;
858}
859
860#else /* !defined(DONT_USE_ALARM_THREAD) */
861
862int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
863{
864 printf("thr_alarm disabled with DONT_USE_THR_ALARM\n");
865 exit(1);
866}
867
868#endif /* !defined(DONT_USE_ALARM_THREAD) */
869#endif /* WIN */
870#endif /* MAIN */
871