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