/* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* To avoid problems with alarms in debug code, we disable DBUG here */ #undef DBUG_OFF #define DBUG_OFF #include #if defined(THREAD) && !defined(DONT_USE_THR_ALARM) #include #include #include #include #include #include #include "thr_alarm.h" #ifdef HAVE_SYS_SELECT_H #include /* AIX needs this for fd_set */ #endif #ifndef ETIME #define ETIME ETIMEDOUT #endif static int alarm_aborted=1; /* No alarm thread */ my_bool thr_alarm_inited= 0; volatile my_bool alarm_thread_running= 0; static sig_handler process_alarm_part2(int sig); #if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) static pthread_mutex_t LOCK_alarm; static pthread_cond_t COND_alarm; static sigset_t full_signal_set; static QUEUE alarm_queue; static uint max_used_alarms=0; pthread_t alarm_thread; #ifdef USE_ALARM_THREAD static void *alarm_handler(void *arg); #define reschedule_alarms() pthread_cond_signal(&COND_alarm) #else #define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM) #endif #if THR_CLIENT_ALARM != SIGALRM || defined(USE_ALARM_THREAD) static sig_handler thread_alarm(int sig __attribute__((unused))); #endif static int compare_ulong(void *not_used __attribute__((unused)), byte *a_ptr,byte* b_ptr) { ulong a=*((ulong*) a_ptr),b= *((ulong*) b_ptr); return (a < b) ? -1 : (a == b) ? 0 : 1; } void init_thr_alarm(uint max_alarms) { sigset_t s; DBUG_ENTER("init_thr_alarm"); alarm_aborted=0; init_queue(&alarm_queue,max_alarms+1,offsetof(ALARM,expire_time),0, compare_ulong,NullS); sigfillset(&full_signal_set); /* Neaded to block signals */ pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_alarm,NULL); #if THR_CLIENT_ALARM != SIGALRM || defined(USE_ALARM_THREAD) my_sigset(THR_CLIENT_ALARM,thread_alarm); #endif sigemptyset(&s); sigaddset(&s, THR_SERVER_ALARM); alarm_thread=pthread_self(); #if defined(USE_ALARM_THREAD) { pthread_attr_t thr_attr; pthread_attr_init(&thr_attr); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&thr_attr,8196); my_pthread_attr_setprio(&thr_attr,100); /* Very high priority */ VOID(pthread_create(&alarm_thread,&thr_attr,alarm_handler,NULL)); VOID(pthread_attr_destroy(&thr_attr)); } #elif defined(USE_ONE_SIGNAL_HAND) pthread_sigmask(SIG_BLOCK, &s, NULL); /* used with sigwait() */ #if THR_SERVER_ALARM == THR_CLIENT_ALARM my_sigset(THR_CLIENT_ALARM,process_alarm); /* Linuxthreads */ pthread_sigmask(SIG_UNBLOCK, &s, NULL); #endif #else my_sigset(THR_SERVER_ALARM, process_alarm); pthread_sigmask(SIG_UNBLOCK, &s, NULL); #endif DBUG_VOID_RETURN; } void resize_thr_alarm(uint max_alarms) { pthread_mutex_lock(&LOCK_alarm); /* It's ok not to shrink the queue as there may be more pending alarms than than max_alarms */ if (alarm_queue.elements < max_alarms) resize_queue(&alarm_queue,max_alarms+1); pthread_mutex_unlock(&LOCK_alarm); } /* Request alarm after sec seconds. SYNOPSIS thr_alarm() alrm Pointer to alarm detection alarm_data Structure to store in alarm queue NOTES This function can't be called from the alarm-handling thread. RETURN VALUES 0 ok 1 If no more alarms are allowed (aborted by process) Stores in first argument a pointer to a non-zero int which is set to 0 when the alarm has been given */ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data) { ulong now; sigset_t old_mask; my_bool reschedule; DBUG_ENTER("thr_alarm"); DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec)); now=(ulong) time((time_t*) 0); pthread_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); pthread_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */ if (alarm_aborted > 0) { /* No signal thread */ DBUG_PRINT("info", ("alarm aborted")); *alrm= 0; /* No alarm */ pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); DBUG_RETURN(1); } if (alarm_aborted < 0) sec= 1; /* Abort mode */ if (alarm_queue.elements >= max_used_alarms) { if (alarm_queue.elements == alarm_queue.max_elements) { DBUG_PRINT("info", ("alarm queue full")); fprintf(stderr,"Warning: thr_alarm queue is full\n"); *alrm= 0; /* No alarm */ pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); DBUG_RETURN(1); } max_used_alarms=alarm_queue.elements+1; } reschedule= (!alarm_queue.elements || (int) (((ALARM*) queue_top(&alarm_queue))->expire_time - now) > (int) sec); if (!alarm_data) { if (!(alarm_data=(ALARM*) my_malloc(sizeof(ALARM),MYF(MY_WME)))) { DBUG_PRINT("info", ("failed my_malloc()")); *alrm= 0; /* No alarm */ pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); DBUG_RETURN(1); } alarm_data->malloced=1; } else alarm_data->malloced=0; alarm_data->expire_time=now+sec; alarm_data->alarmed=0; alarm_data->thread=pthread_self(); queue_insert(&alarm_queue,(byte*) alarm_data); /* Reschedule alarm if the current one has more than sec left */ if (reschedule) { DBUG_PRINT("info", ("reschedule")); if (pthread_equal(pthread_self(),alarm_thread)) alarm(sec); /* purecov: inspected */ else reschedule_alarms(); /* Reschedule alarms */ } pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); (*alrm)= &alarm_data->alarmed; DBUG_RETURN(0); } /* Remove alarm from list of alarms */ void thr_end_alarm(thr_alarm_t *alarmed) { ALARM *alarm_data; sigset_t old_mask; uint i, found=0; DBUG_ENTER("thr_end_alarm"); pthread_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); pthread_mutex_lock(&LOCK_alarm); alarm_data= (ALARM*) ((byte*) *alarmed - offsetof(ALARM,alarmed)); for (i=0 ; i < alarm_queue.elements ; i++) { if ((ALARM*) queue_element(&alarm_queue,i) == alarm_data) { queue_remove(&alarm_queue,i),MYF(0); if (alarm_data->malloced) my_free((gptr) alarm_data,MYF(0)); found++; #ifdef DBUG_OFF break; #endif } } DBUG_ASSERT(!*alarmed || found == 1); if (!found) { if (*alarmed) fprintf(stderr,"Warning: Didn't find alarm 0x%lx in queue of %d alarms\n", (long) *alarmed, alarm_queue.elements); DBUG_PRINT("warning",("Didn't find alarm 0x%lx in queue\n", (long) *alarmed)); } pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); DBUG_VOID_RETURN; } /* Come here when some alarm in queue is due. Mark all alarms with are finnished in list. Shedule alarms to be sent again after 1-10 sec (many alarms at once) If alarm_aborted is set then all alarms are given and resent every second. */ sig_handler process_alarm(int sig __attribute__((unused))) { sigset_t old_mask; /* This must be first as we can't call DBUG inside an alarm for a normal thread */ #if THR_SERVER_ALARM == THR_CLIENT_ALARM if (!pthread_equal(pthread_self(),alarm_thread)) { #if defined(MAIN) && !defined(__bsdi__) printf("thread_alarm\n"); fflush(stdout); #endif #ifdef DONT_REMEMBER_SIGNAL my_sigset(THR_CLIENT_ALARM,process_alarm); /* int. thread system calls */ #endif return; } #endif /* We have to do do the handling of the alarm in a sub function, because otherwise we would get problems with two threads calling DBUG_... functions at the same time (as two threads may call process_alarm() at the same time */ #ifndef USE_ALARM_THREAD pthread_sigmask(SIG_SETMASK,&full_signal_set,&old_mask); pthread_mutex_lock(&LOCK_alarm); #endif process_alarm_part2(sig); #ifndef USE_ALARM_THREAD #if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND) my_sigset(THR_SERVER_ALARM,process_alarm); #endif pthread_mutex_unlock(&LOCK_alarm); pthread_sigmask(SIG_SETMASK,&old_mask,NULL); #endif return; } static sig_handler process_alarm_part2(int sig __attribute__((unused))) { ALARM *alarm_data; DBUG_ENTER("process_alarm"); DBUG_PRINT("info",("sig: %d active alarms: %d",sig,alarm_queue.elements)); #if defined(MAIN) && !defined(__bsdi__) printf("process_alarm\n"); fflush(stdout); #endif if (alarm_queue.elements) { if (alarm_aborted) { uint i; for (i=0 ; i < alarm_queue.elements ;) { alarm_data=(ALARM*) queue_element(&alarm_queue,i); alarm_data->alarmed=1; /* Info to thread */ if (pthread_equal(alarm_data->thread,alarm_thread) || pthread_kill(alarm_data->thread, THR_CLIENT_ALARM)) { #ifdef MAIN printf("Warning: pthread_kill couldn't find thread!!!\n"); #endif queue_remove(&alarm_queue,i); /* No thread. Remove alarm */ } else i++; /* Signal next thread */ } #ifndef USE_ALARM_THREAD if (alarm_queue.elements) alarm(1); /* Signal soon again */ #endif } else { ulong now=(ulong) time((time_t*) 0); ulong next=now+10-(now%10); while ((alarm_data=(ALARM*) queue_top(&alarm_queue))->expire_time <= now) { alarm_data->alarmed=1; /* Info to thread */ DBUG_PRINT("info",("sending signal to waiting thread")); if (pthread_equal(alarm_data->thread,alarm_thread) || pthread_kill(alarm_data->thread, THR_CLIENT_ALARM)) { #ifdef MAIN printf("Warning: pthread_kill couldn't find thread!!!\n"); #endif queue_remove(&alarm_queue,0); /* No thread. Remove alarm */ if (!alarm_queue.elements) break; } else { alarm_data->expire_time=next; queue_replaced(&alarm_queue); } } #ifndef USE_ALARM_THREAD if (alarm_queue.elements) { #ifdef __bsdi__ alarm(0); /* Remove old alarm */ #endif alarm((uint) (alarm_data->expire_time-now)); } #endif } } DBUG_VOID_RETURN; } /* Schedule all alarms now and optionally free all structures SYNPOSIS end_thr_alarm() free_structures Set to 1 if we should free memory used for the alarm queue. When we call this we should KNOW that there is no active alarms IMPLEMENTATION Set alarm_abort to -1 which will change the behavior of alarms as follows: - All old alarms will be rescheduled at once - All new alarms will be rescheduled to one second */ void end_thr_alarm(my_bool free_structures) { DBUG_ENTER("end_thr_alarm"); if (alarm_aborted != 1) /* If memory not freed */ { pthread_mutex_lock(&LOCK_alarm); DBUG_PRINT("info",("Resheduling %d waiting alarms",alarm_queue.elements)); alarm_aborted= -1; /* mark aborted */ if (alarm_queue.elements || (alarm_thread_running && free_structures)) { if (pthread_equal(pthread_self(),alarm_thread)) alarm(1); /* Shut down everything soon */ else reschedule_alarms(); } if (free_structures) { struct timespec abstime; /* The following test is just for safety, the caller should not depend on this */ DBUG_ASSERT(!alarm_queue.elements); /* Wait until alarm thread dies */ set_timespec(abstime, 10); /* Wait up to 10 seconds */ while (alarm_thread_running) { int error= pthread_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime); if (error == ETIME || error == ETIMEDOUT) break; /* Don't wait forever */ } if (!alarm_queue.elements) { delete_queue(&alarm_queue); alarm_aborted= 1; pthread_mutex_unlock(&LOCK_alarm); if (!alarm_thread_running) /* Safety */ { pthread_mutex_destroy(&LOCK_alarm); pthread_cond_destroy(&COND_alarm); } } } else pthread_mutex_unlock(&LOCK_alarm); } DBUG_VOID_RETURN; } /* Remove another thread from the alarm */ void thr_alarm_kill(pthread_t thread_id) { uint i; if (alarm_aborted) return; pthread_mutex_lock(&LOCK_alarm); for (i=0 ; i < alarm_queue.elements ; i++) { if (pthread_equal(((ALARM*) queue_element(&alarm_queue,i))->thread, thread_id)) { ALARM *tmp=(ALARM*) queue_remove(&alarm_queue,i); tmp->expire_time=0; queue_insert(&alarm_queue,(byte*) tmp); reschedule_alarms(); break; } } pthread_mutex_unlock(&LOCK_alarm); } void thr_alarm_info(ALARM_INFO *info) { pthread_mutex_lock(&LOCK_alarm); info->next_alarm_time= 0; info->max_used_alarms= max_used_alarms; if ((info->active_alarms= alarm_queue.elements)) { ulong now=(ulong) time((time_t*) 0); long time_diff; ALARM *alarm_data= (ALARM*) queue_top(&alarm_queue); time_diff= (long) (alarm_data->expire_time - now); info->next_alarm_time= (ulong) (time_diff < 0 ? 0 : time_diff); } pthread_mutex_unlock(&LOCK_alarm); } /* This is here for thread to get interruptet from read/write/fcntl ARGSUSED */ #if THR_CLIENT_ALARM != SIGALRM || defined(USE_ALARM_THREAD) static sig_handler thread_alarm(int sig) { #ifdef MAIN printf("thread_alarm\n"); fflush(stdout); #endif #ifdef DONT_REMEMBER_SIGNAL my_sigset(sig,thread_alarm); /* int. thread system calls */ #endif } #endif #ifdef HAVE_TIMESPEC_TS_SEC #define tv_sec ts_sec #define tv_nsec ts_nsec #endif /* set up a alarm thread with uses 'sleep' to sleep between alarms */ #ifdef USE_ALARM_THREAD static void *alarm_handler(void *arg __attribute__((unused))) { int error; struct timespec abstime; #ifdef MAIN puts("Starting alarm thread"); #endif my_thread_init(); alarm_thread_running= 1; pthread_mutex_lock(&LOCK_alarm); for (;;) { if (alarm_queue.elements) { ulong sleep_time,now=time((time_t*) 0); if (alarm_aborted) sleep_time=now+1; else sleep_time= ((ALARM*) queue_top(&alarm_queue))->expire_time; if (sleep_time > now) { abstime.tv_sec=sleep_time; abstime.tv_nsec=0; if ((error=pthread_cond_timedwait(&COND_alarm,&LOCK_alarm,&abstime)) && error != ETIME && error != ETIMEDOUT) { #ifdef MAIN printf("Got error: %d from ptread_cond_timedwait (errno: %d)\n", error,errno); #endif } } } else if (alarm_aborted == -1) break; else if ((error=pthread_cond_wait(&COND_alarm,&LOCK_alarm))) { #ifdef MAIN printf("Got error: %d from ptread_cond_wait (errno: %d)\n", error,errno); #endif } process_alarm(0); } bzero((char*) &alarm_thread,sizeof(alarm_thread)); /* For easy debugging */ alarm_thread_running= 0; pthread_cond_signal(&COND_alarm); pthread_mutex_unlock(&LOCK_alarm); pthread_exit(0); return 0; /* Impossible */ } #endif /* USE_ALARM_THREAD */ /***************************************************************************** thr_alarm for OS/2 *****************************************************************************/ #elif defined(__EMX__) || defined(OS2) #define INCL_BASE #define INCL_NOPMAPI #include static pthread_mutex_t LOCK_alarm; static sigset_t full_signal_set; static QUEUE alarm_queue; pthread_t alarm_thread; #ifdef USE_ALARM_THREAD static pthread_cond_t COND_alarm; static void *alarm_handler(void *arg); #define reschedule_alarms() pthread_cond_signal(&COND_alarm) #else #define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM) #endif sig_handler process_alarm(int sig __attribute__((unused))) { sigset_t old_mask; ALARM *alarm_data; DBUG_PRINT("info",("sig: %d active alarms: %d",sig,alarm_queue.elements)); } /* Remove another thread from the alarm */ void thr_alarm_kill(pthread_t thread_id) { uint i; pthread_mutex_lock(&LOCK_alarm); for (i=0 ; i < alarm_queue.elements ; i++) { if (pthread_equal(((ALARM*) queue_element(&alarm_queue,i))->thread, thread_id)) { ALARM *tmp=(ALARM*) queue_remove(&alarm_queue,i); tmp->expire_time=0; queue_insert(&alarm_queue,(byte*) tmp); reschedule_alarms(); break; } } pthread_mutex_unlock(&LOCK_alarm); } bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm) { APIRET rc; if (alarm_aborted) { alarm->alarmed.crono=0; alarm->alarmed.event=0; return 1; } if (rc = DosCreateEventSem(NULL,(HEV *) &alarm->alarmed.event, DC_SEM_SHARED,FALSE)) { printf("Error creating event semaphore! [%d] \n",rc); alarm->alarmed.crono=0; alarm->alarmed.event=0; return 1; } if (rc = DosAsyncTimer((long) sec*1000L, (HSEM) alarm->alarmed.event, (HTIMER *) &alarm->alarmed.crono)) { printf("Error starting async timer! [%d] \n",rc); DosCloseEventSem((HEV) alarm->alarmed.event); alarm->alarmed.crono=0; alarm->alarmed.event=0; return 1; } /* endif */ (*alrm)= &alarm->alarmed; return 1; } bool thr_got_alarm(thr_alarm_t *alrm_ptr) { thr_alarm_t alrm= *alrm_ptr; APIRET rc; if (alrm->crono) { rc = DosWaitEventSem((HEV) alrm->event, SEM_IMMEDIATE_RETURN); if (rc == 0) { DosCloseEventSem((HEV) alrm->event); alrm->crono = 0; alrm->event = 0; } /* endif */ } return !alrm->crono || alarm_aborted; } void thr_end_alarm(thr_alarm_t *alrm_ptr) { thr_alarm_t alrm= *alrm_ptr; if (alrm->crono) { DosStopTimer((HTIMER) alrm->crono); DosCloseEventSem((HEV) alrm->event); alrm->crono = 0; alrm->event = 0; } } void end_thr_alarm(my_bool free_structures) { DBUG_ENTER("end_thr_alarm"); alarm_aborted=1; /* No more alarms */ DBUG_VOID_RETURN; } void init_thr_alarm(uint max_alarm) { DBUG_ENTER("init_thr_alarm"); alarm_aborted=0; /* Yes, Gimmie alarms */ DBUG_VOID_RETURN; } void thr_alarm_info(ALARM_INFO *info) { bzero((char*) info, sizeof(*info)); } void resize_thr_alarm(uint max_alarms) { } /***************************************************************************** thr_alarm for win95 *****************************************************************************/ #else /* __WIN__ */ void thr_alarm_kill(pthread_t thread_id) { /* Can't do this yet */ } sig_handler process_alarm(int sig __attribute__((unused))) { /* Can't do this yet */ } my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm) { (*alrm)= &alarm->alarmed; if (alarm_aborted) { alarm->alarmed.crono=0; return 1; } if (!(alarm->alarmed.crono=SetTimer((HWND) NULL,0, sec*1000, (TIMERPROC) NULL))) return 1; return 0; } bool thr_got_alarm(thr_alarm_t *alrm_ptr) { thr_alarm_t alrm= *alrm_ptr; MSG msg; if (alrm->crono) { PeekMessage(&msg,NULL,WM_TIMER,WM_TIMER,PM_REMOVE) ; if (msg.message == WM_TIMER || alarm_aborted) { KillTimer(NULL, alrm->crono); alrm->crono = 0; } } return !alrm->crono || alarm_aborted; } void thr_end_alarm(thr_alarm_t *alrm_ptr) { thr_alarm_t alrm= *alrm_ptr; /* alrm may be zero if thr_alarm aborted with an error */ if (alrm && alrm->crono) { KillTimer(NULL, alrm->crono); alrm->crono = 0; } } void end_thr_alarm(my_bool free_structures) { DBUG_ENTER("end_thr_alarm"); alarm_aborted=1; /* No more alarms */ DBUG_VOID_RETURN; } void init_thr_alarm(uint max_alarm) { DBUG_ENTER("init_thr_alarm"); alarm_aborted=0; /* Yes, Gimmie alarms */ DBUG_VOID_RETURN; } void thr_alarm_info(ALARM_INFO *info) { bzero((char*) info, sizeof(*info)); } void resize_thr_alarm(uint max_alarms) { } #endif /* __WIN__ */ #endif /* THREAD */ /**************************************************************************** Handling of test case (when compiled with -DMAIN) ***************************************************************************/ #ifdef MAIN #if defined(THREAD) && !defined(DONT_USE_THR_ALARM) static pthread_cond_t COND_thread_count; static pthread_mutex_t LOCK_thread_count; static uint thread_count; #ifdef HPUX10 typedef int * fd_set_ptr; #else typedef fd_set * fd_set_ptr; #endif /* HPUX10 */ static void *test_thread(void *arg) { int i,param=*((int*) arg),wait_time,retry; time_t start_time; thr_alarm_t got_alarm; fd_set fd; FD_ZERO(&fd); my_thread_init(); printf("Thread %d (%s) started\n",param,my_thread_name()); fflush(stdout); for (i=1 ; i <= 10 ; i++) { wait_time=param ? 11-i : i; start_time=time((time_t*) 0); if (thr_alarm(&got_alarm,wait_time,0)) { printf("Thread: %s Alarms aborted\n",my_thread_name()); break; } if (wait_time == 3) { printf("Thread: %s Simulation of no alarm needed\n",my_thread_name()); fflush(stdout); } else { for (retry=0 ; !thr_got_alarm(&got_alarm) && retry < 10 ; retry++) { printf("Thread: %s Waiting %d sec\n",my_thread_name(),wait_time); select(0,(fd_set_ptr) &fd,0,0,0); } if (!thr_got_alarm(&got_alarm)) { printf("Thread: %s didn't get an alarm. Aborting!\n", my_thread_name()); break; } if (wait_time == 7) { /* Simulate alarm-miss */ fd_set readFDs; uint max_connection=fileno(stdin); FD_ZERO(&readFDs); FD_SET(max_connection,&readFDs); retry=0; for (;;) { printf("Thread: %s Simulating alarm miss\n",my_thread_name()); fflush(stdout); if (select(max_connection+1, (fd_set_ptr) &readFDs,0,0,0) < 0) { if (errno == EINTR) break; /* Got new interrupt */ printf("Got errno: %d from select. Retrying..\n",errno); if (retry++ >= 3) { printf("Warning: Interrupt of select() doesn't set errno!\n"); break; } } else /* This shouldn't happen */ { if (!FD_ISSET(max_connection,&readFDs)) { printf("Select interrupted, but errno not set\n"); fflush(stdout); if (retry++ >= 3) break; continue; } VOID(getchar()); /* Somebody was playing */ } } } } printf("Thread: %s Slept for %d (%d) sec\n",my_thread_name(), (int) (time((time_t*) 0)-start_time), wait_time); fflush(stdout); thr_end_alarm(&got_alarm); fflush(stdout); } pthread_mutex_lock(&LOCK_thread_count); thread_count--; VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */ pthread_mutex_unlock(&LOCK_thread_count); free((gptr) arg); return 0; } #ifdef USE_ONE_SIGNAL_HAND static sig_handler print_signal_warning(int sig) { printf("Warning: Got signal %d from thread %s\n",sig,my_thread_name()); fflush(stdout); #ifdef DONT_REMEMBER_SIGNAL my_sigset(sig,print_signal_warning); /* int. thread system calls */ #endif #ifndef OS2 if (sig == SIGALRM) alarm(2); /* reschedule alarm */ #endif } #endif /* USE_ONE_SIGNAL_HAND */ static void *signal_hand(void *arg __attribute__((unused))) { sigset_t set; int sig,error,err_count=0;; my_thread_init(); pthread_detach_this_thread(); init_thr_alarm(10); /* Setup alarm handler */ pthread_mutex_lock(&LOCK_thread_count); /* Required by bsdi */ VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */ pthread_mutex_unlock(&LOCK_thread_count); #ifndef OS2 sigemptyset(&set); /* Catch all signals */ sigaddset(&set,SIGINT); sigaddset(&set,SIGQUIT); sigaddset(&set,SIGTERM); #if THR_CLIENT_ALARM != SIGHUP sigaddset(&set,SIGHUP); #endif #ifdef SIGTSTP sigaddset(&set,SIGTSTP); #endif #ifdef USE_ONE_SIGNAL_HAND sigaddset(&set,THR_SERVER_ALARM); /* For alarms */ puts("Starting signal and alarm handling thread"); #else puts("Starting signal handling thread"); #endif #endif /* OS2 */ printf("server alarm: %d thread alarm: %d\n", THR_SERVER_ALARM,THR_CLIENT_ALARM); DBUG_PRINT("info",("Starting signal and alarm handling thread")); for(;;) { while ((error=my_sigwait(&set,&sig)) == EINTR) printf("sigwait restarted\n"); if (error) { fprintf(stderr,"Got error %d from sigwait\n",error); if (err_count++ > 5) exit(1); /* Too many errors in test */ continue; } #ifdef USE_ONE_SIGNAL_HAND if (sig != THR_SERVER_ALARM) #endif printf("Main thread: Got signal %d\n",sig); switch (sig) { case SIGINT: case SIGQUIT: case SIGTERM: #ifndef OS2 case SIGHUP: #endif printf("Aborting nicely\n"); end_thr_alarm(0); break; #ifdef SIGTSTP case SIGTSTP: printf("Aborting\n"); exit(1); return 0; /* Keep some compilers happy */ #endif #ifndef OS2 #ifdef USE_ONE_SIGNAL_HAND case THR_SERVER_ALARM: process_alarm(sig); break; #endif #endif /* OS2 */ } } } int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) { pthread_t tid; pthread_attr_t thr_attr; int i,*param,error; sigset_t set; ALARM_INFO alarm_info; MY_INIT(argv[0]); if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#') DBUG_PUSH(argv[1]+2); pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_thread_count,NULL); /* Start a alarm handling thread */ #ifndef OS2 sigemptyset(&set); sigaddset(&set,SIGINT); sigaddset(&set,SIGQUIT); sigaddset(&set,SIGTERM); sigaddset(&set,SIGHUP); signal(SIGTERM,SIG_DFL); /* If it's blocked by parent */ #ifdef SIGTSTP sigaddset(&set,SIGTSTP); #endif sigaddset(&set,THR_SERVER_ALARM); sigdelset(&set,THR_CLIENT_ALARM); (void) pthread_sigmask(SIG_SETMASK,&set,NULL); #ifdef NOT_USED sigemptyset(&set); sigaddset(&set,THR_CLIENT_ALARM); VOID(pthread_sigmask(SIG_UNBLOCK, &set, (sigset_t*) 0)); #endif #endif /* OS2 */ pthread_attr_init(&thr_attr); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&thr_attr,65536L); /* Start signal thread and wait for it to start */ VOID(pthread_mutex_lock(&LOCK_thread_count)); pthread_create(&tid,&thr_attr,signal_hand,NULL); VOID(pthread_cond_wait(&COND_thread_count,&LOCK_thread_count)); VOID(pthread_mutex_unlock(&LOCK_thread_count)); DBUG_PRINT("info",("signal thread created")); thr_setconcurrency(3); pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); printf("Main thread: %s\n",my_thread_name()); for (i=0 ; i < 2 ; i++) { param=(int*) malloc(sizeof(int)); *param= i; pthread_mutex_lock(&LOCK_thread_count); if ((error=pthread_create(&tid,&thr_attr,test_thread,(void*) param))) { printf("Can't create thread %d, error: %d\n",i,error); exit(1); } thread_count++; pthread_mutex_unlock(&LOCK_thread_count); } pthread_attr_destroy(&thr_attr); pthread_mutex_lock(&LOCK_thread_count); thr_alarm_info(&alarm_info); printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n", alarm_info.active_alarms, alarm_info.max_used_alarms, alarm_info.next_alarm_time); while (thread_count) { VOID(pthread_cond_wait(&COND_thread_count,&LOCK_thread_count)); if (thread_count == 1) { printf("Calling end_thr_alarm. This should cancel the last thread\n"); end_thr_alarm(0); } } pthread_mutex_unlock(&LOCK_thread_count); end_thr_alarm(1); thr_alarm_info(&alarm_info); printf("Main_thread: Alarms: %u max_alarms: %u next_alarm_time: %lu\n", alarm_info.active_alarms, alarm_info.max_used_alarms, alarm_info.next_alarm_time); printf("Test succeeded\n"); return 0; } #else /* THREAD */ int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) { #ifndef THREAD printf("thr_alarm disabled because we are not using threads\n"); #else printf("thr_alarm disabled with DONT_USE_THR_ALARM\n"); #endif exit(1); } #endif /* THREAD */ #endif /* MAIN */