/* Copyright (c) 2000, 2013, Oracle and/or its affiliates Copyright (c) 2012, 2014, SkySQL 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; version 2 of the License. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ /* To avoid problems with alarms in debug code, we disable DBUG here */ #define FORCE_DBUG_OFF #include "mysys_priv.h" #include #if !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 #ifdef DBUG_OFF #define reset_index_in_queue(alarm_data) #else #define reset_index_in_queue(alarm_data) alarm_data->index_in_queue= 0; #endif /* DBUG_OFF */ #ifndef USE_ONE_SIGNAL_HAND #define one_signal_hand_sigmask(A,B,C) pthread_sigmask((A), (B), (C)) #else #define one_signal_hand_sigmask(A,B,C) #endif my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0; #if !defined(_WIN32) uint thr_client_alarm; static int alarm_aborted=1; /* No alarm thread */ volatile my_bool alarm_thread_running= 0; time_t next_alarm_expire_time= ~ (time_t) 0; static sig_handler process_alarm_part2(int sig); static mysql_mutex_t LOCK_alarm; static mysql_cond_t COND_alarm; static sigset_t full_signal_set; static QUEUE alarm_queue; static uint max_used_alarms=0; pthread_t alarm_thread; #define MY_THR_ALARM_QUEUE_EXTENT 10 #ifdef USE_ALARM_THREAD static void *alarm_handler(void *arg); #define reschedule_alarms() mysql_cond_signal(&COND_alarm) #else #define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM) #endif static sig_handler thread_alarm(int sig __attribute__((unused))); static int compare_ulong(void *not_used __attribute__((unused)), uchar *a_ptr,uchar* 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; next_alarm_expire_time= ~ (time_t) 0; init_queue(&alarm_queue, max_alarms+1, offsetof(ALARM,expire_time), 0, compare_ulong, NullS, offsetof(ALARM, index_in_queue)+1, MY_THR_ALARM_QUEUE_EXTENT); sigfillset(&full_signal_set); /* Neaded to block signals */ mysql_mutex_init(key_LOCK_alarm, &LOCK_alarm, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_alarm, &COND_alarm, NULL); thr_client_alarm= SIGUSR1; my_sigset(thr_client_alarm, thread_alarm); 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); mysql_thread_create(key_thread_alarm, &alarm_thread, &thr_attr, alarm_handler, NULL); pthread_attr_destroy(&thr_attr); } #elif defined(USE_ONE_SIGNAL_HAND) pthread_sigmask(SIG_BLOCK, &s, NULL); /* used with sigwait() */ #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) { mysql_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); max_used_alarms= alarm_queue.elements; } mysql_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) { time_t now, next; #ifndef USE_ONE_SIGNAL_HAND sigset_t old_mask; #endif my_bool reschedule; struct st_my_thread_var *current_my_thread_var= my_thread_var; DBUG_ENTER("thr_alarm"); DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec)); if (my_disable_thr_alarm) { (*alrm)= &alarm_data->alarmed; alarm_data->alarmed= 1; /* Abort if interrupted */ DBUG_RETURN(0); } if (unlikely(alarm_aborted)) { /* No signal thread */ DBUG_PRINT("info", ("alarm aborted")); if (alarm_aborted > 0) goto abort_no_unlock; sec= 1; /* Abort mode */ } now= my_time(0); if (!alarm_data) { if (!(alarm_data=(ALARM*) my_malloc(PSI_INSTRUMENT_ME, sizeof(ALARM), MYF(MY_WME)))) goto abort_no_unlock; alarm_data->malloced= 1; } else alarm_data->malloced= 0; next= now + sec; alarm_data->expire_time= next; alarm_data->alarmed= 0; alarm_data->thread= current_my_thread_var->pthread_self; alarm_data->thread_id= current_my_thread_var->id; one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); mysql_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */ if (alarm_queue.elements >= max_used_alarms) { max_used_alarms=alarm_queue.elements+1; } reschedule= (ulong) next_alarm_expire_time > (ulong) next; queue_insert_safe(&alarm_queue, (uchar*) alarm_data); assert(alarm_data->index_in_queue > 0); /* Reschedule alarm if the current one has more than sec left */ if (unlikely(reschedule)) { DBUG_PRINT("info", ("reschedule")); if (pthread_equal(pthread_self(),alarm_thread)) { alarm(sec); /* purecov: inspected */ next_alarm_expire_time= next; } else reschedule_alarms(); /* Reschedule alarms */ } mysql_mutex_unlock(&LOCK_alarm); one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL); (*alrm)= &alarm_data->alarmed; DBUG_RETURN(0); abort_no_unlock: *alrm= 0; /* No alarm */ DBUG_RETURN(1); } /* Remove alarm from list of alarms */ void thr_end_alarm(thr_alarm_t *alarmed) { ALARM *alarm_data; #ifndef USE_ONE_SIGNAL_HAND sigset_t old_mask; #endif DBUG_ENTER("thr_end_alarm"); if (my_disable_thr_alarm) DBUG_VOID_RETURN; one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask); alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed)); mysql_mutex_lock(&LOCK_alarm); DBUG_ASSERT(alarm_data->index_in_queue != 0); DBUG_ASSERT((ALARM*) queue_element(&alarm_queue, alarm_data->index_in_queue) == alarm_data); queue_remove(&alarm_queue, alarm_data->index_in_queue); mysql_mutex_unlock(&LOCK_alarm); one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL); reset_index_in_queue(alarm_data); DBUG_VOID_RETURN; } /* Come here when some alarm in queue is due. Mark all alarms with are finnished in list. Schedule 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 */ /* 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); mysql_mutex_lock(&LOCK_alarm); #endif process_alarm_part2(sig); #ifndef USE_ALARM_THREAD #if defined(SIGNAL_HANDLER_RESET_ON_DELIVERY) && !defined(USE_ONE_SIGNAL_HAND) my_sigset(THR_SERVER_ALARM,process_alarm); #endif mysql_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 (likely(alarm_queue.elements)) { if (unlikely(alarm_aborted)) { uint i; for (i= queue_first_element(&alarm_queue) ; i <= queue_last_element(&alarm_queue) ;) { 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 */ reset_index_in_queue(alarm_data); } else i++; /* Signal next thread */ } #ifndef USE_ALARM_THREAD if (alarm_queue.elements) alarm(1); /* Signal soon again */ #endif } else { time_t now= my_time(0); time_t 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 /* MAIN */ queue_remove_top(&alarm_queue); /* No thread. Remove alarm */ reset_index_in_queue(alarm_data); if (!alarm_queue.elements) break; } else { alarm_data->expire_time=next; queue_replace_top(&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)); next_alarm_expire_time= alarm_data->expire_time; } #endif } } else { /* Ensure that next time we call thr_alarm(), we will schedule a new alarm */ next_alarm_expire_time= ~(time_t) 0; } 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 */ { mysql_mutex_lock(&LOCK_alarm); DBUG_PRINT("info",("Rescheduling %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; 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= mysql_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime); if (error == ETIME || error == ETIMEDOUT) break; /* Don't wait forever */ } delete_queue(&alarm_queue); alarm_aborted= 1; mysql_mutex_unlock(&LOCK_alarm); if (!alarm_thread_running) /* Safety */ { mysql_mutex_destroy(&LOCK_alarm); mysql_cond_destroy(&COND_alarm); } } else mysql_mutex_unlock(&LOCK_alarm); } DBUG_VOID_RETURN; } /* Remove another thread from the alarm */ void thr_alarm_kill(my_thread_id thread_id) { uint i; DBUG_ENTER("thr_alarm_kill"); if (alarm_aborted) return; mysql_mutex_lock(&LOCK_alarm); for (i= queue_first_element(&alarm_queue) ; i <= queue_last_element(&alarm_queue); i++) { ALARM *element= (ALARM*) queue_element(&alarm_queue,i); if (element->thread_id == thread_id) { DBUG_PRINT("info", ("found thread; Killing it")); element->expire_time= 0; queue_replace(&alarm_queue, i); reschedule_alarms(); break; } } mysql_mutex_unlock(&LOCK_alarm); DBUG_VOID_RETURN; } void thr_alarm_info(ALARM_INFO *info) { mysql_mutex_lock(&LOCK_alarm); info->next_alarm_time= 0; info->max_used_alarms= max_used_alarms; if ((info->active_alarms= alarm_queue.elements)) { time_t now= my_time(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); } mysql_mutex_unlock(&LOCK_alarm); } /* This is here for thread to get interruptet from read/write/fcntl ARGSUSED */ static sig_handler thread_alarm(int sig __attribute__((unused))) { #ifdef MAIN printf("thread_alarm\n"); fflush(stdout); #endif #ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY my_sigset(sig,thread_alarm); /* int. thread system calls */ #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; mysql_mutex_lock(&LOCK_alarm); for (;;) { if (alarm_queue.elements) { time_t sleep_time,now= my_time(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; next_alarm_expire_time= sleep_time; if ((error= mysql_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 { next_alarm_expire_time= ~ (time_t) 0; if ((error= mysql_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; mysql_cond_signal(&COND_alarm); mysql_mutex_unlock(&LOCK_alarm); pthread_exit(0); return 0; /* Impossible */ } #endif /* USE_ALARM_THREAD */ #endif /**************************************************************************** Handling of test case (when compiled with -DMAIN) ***************************************************************************/ #ifdef MAIN #if !defined(DONT_USE_THR_ALARM) static mysql_cond_t COND_thread_count; static mysql_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= my_time(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) (my_time(0)-start_time), wait_time); fflush(stdout); thr_end_alarm(&got_alarm); fflush(stdout); } mysql_mutex_lock(&LOCK_thread_count); thread_count--; mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */ mysql_mutex_unlock(&LOCK_thread_count); my_thread_end(); return 0; } 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 */ mysql_mutex_lock(&LOCK_thread_count); /* Required by bsdi */ mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */ mysql_mutex_unlock(&LOCK_thread_count); sigemptyset(&set); /* Catch all signals */ sigaddset(&set,SIGINT); sigaddset(&set,SIGQUIT); sigaddset(&set,SIGTERM); sigaddset(&set,SIGHUP); #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 printf("server alarm: %d thread alarm: %d\n", THR_SERVER_ALARM, thr_client_alarm); DBUG_PRINT("info",("Starting signal and alarm handling thread")); for(;;) { int code; while ((error=my_sigwait(&set,&sig,&code)) == 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: case SIGHUP: 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 #ifdef USE_ONE_SIGNAL_HAND case THR_SERVER_ALARM: process_alarm(sig); break; #endif } } } int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) { pthread_t tid; pthread_attr_t thr_attr; int i, param[2], 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); } mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST); mysql_cond_init(0, &COND_thread_count, NULL); /* Start a alarm handling thread */ 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); 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 */ mysql_mutex_lock(&LOCK_thread_count); mysql_thread_create(0, &tid, &thr_attr, signal_hand, NULL); mysql_cond_wait(&COND_thread_count, &LOCK_thread_count); mysql_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[i]= i; mysql_mutex_lock(&LOCK_thread_count); if ((error= mysql_thread_create(0, &tid, &thr_attr, test_thread, (void*) ¶m[i]))) { printf("Can't create thread %d, error: %d\n",i,error); exit(1); } thread_count++; mysql_mutex_unlock(&LOCK_thread_count); } pthread_attr_destroy(&thr_attr); mysql_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) { mysql_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); } } mysql_mutex_unlock(&LOCK_thread_count); thr_alarm_info(&alarm_info); end_thr_alarm(1); 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"); mysql_cond_destroy(&COND_thread_count); mysql_mutex_destroy(&LOCK_thread_count); my_end(MY_CHECK_ERROR); return 0; } #else /* !defined(DONT_USE_ALARM_THREAD) */ int main(int argc __attribute__((unused)),char **argv __attribute__((unused))) { printf("thr_alarm disabled with DONT_USE_THR_ALARM\n"); exit(1); } #endif /* !defined(DONT_USE_ALARM_THREAD) */ #endif /* WIN */ #endif /* MAIN */