thr_alarm.c 24.5 KB
Newer Older
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1 2 3 4
/* 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
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6 7

   This program is distributed in the hope that it will be useful,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
9 10 11 12 13 14
   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 */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
15

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
16
/* To avoid problems with alarms in debug code, we disable DBUG here */
17
#define FORCE_DBUG_OFF
18
#include <my_global.h>
19 20

#if defined(THREAD) && !defined(DONT_USE_THR_ALARM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <errno.h>
#include <my_pthread.h>
#include <signal.h>
#include <my_sys.h>
#include <m_string.h>
#include <queues.h>
#include "thr_alarm.h"

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>				/* AIX needs this for fd_set */
#endif

#ifndef ETIME
#define ETIME ETIMEDOUT
#endif

37
uint thr_client_alarm;
38
static int alarm_aborted=1;			/* No alarm thread */
39 40
my_bool thr_alarm_inited= 0;
volatile my_bool alarm_thread_running= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
41

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
42 43
static sig_handler process_alarm_part2(int sig);

44
#if !defined(__WIN__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
45 46

static pthread_mutex_t LOCK_alarm;
47
static pthread_cond_t COND_alarm;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
48 49
static sigset_t full_signal_set;
static QUEUE alarm_queue;
50
static uint max_used_alarms=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
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

static sig_handler thread_alarm(int sig __attribute__((unused)));

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 */
77
  pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST);
78
  pthread_cond_init(&COND_alarm,NULL);
79 80 81 82
  if (thd_lib_detected == THD_LIB_LT)
    thr_client_alarm= SIGALRM;
  else
    thr_client_alarm= SIGUSR1;
83 84
#ifndef USE_ALARM_THREAD
  if (thd_lib_detected != THD_LIB_LT)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
85
#endif
86 87 88
  {
    my_sigset(thr_client_alarm, thread_alarm);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
  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() */
106 107 108 109 110
  if (thd_lib_detected == THD_LIB_LT)
  {
    my_sigset(thr_client_alarm, process_alarm);        /* Linuxthreads */
    pthread_sigmask(SIG_UNBLOCK, &s, NULL);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
111
#else
112
  my_sigset(THR_SERVER_ALARM, process_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
113 114 115 116 117
  pthread_sigmask(SIG_UNBLOCK, &s, NULL);
#endif
  DBUG_VOID_RETURN;
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
118

119 120 121
void resize_thr_alarm(uint max_alarms)
{
  pthread_mutex_lock(&LOCK_alarm);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
122 123 124 125
  /*
    It's ok not to shrink the queue as there may be more pending alarms than
    than max_alarms
  */
126 127 128 129 130
  if (alarm_queue.elements < max_alarms)
    resize_queue(&alarm_queue,max_alarms+1);
  pthread_mutex_unlock(&LOCK_alarm);
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
131

bk@work.mysql.com's avatar
bk@work.mysql.com committed
132
/*
133
  Request alarm after sec seconds.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

  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
bk@work.mysql.com's avatar
bk@work.mysql.com committed
149 150
*/

151
my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
152 153 154 155
{
  ulong now;
  sigset_t old_mask;
  my_bool reschedule;
156
  struct st_my_thread_var *current_my_thread_var= my_thread_var;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
157 158 159 160 161
  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);
162
  pthread_mutex_lock(&LOCK_alarm);        /* Lock from threads & alarms */
163
  if (alarm_aborted > 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
164 165
  {					/* No signal thread */
    DBUG_PRINT("info", ("alarm aborted"));
166
    *alrm= 0;					/* No alarm */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
167 168 169 170
    pthread_mutex_unlock(&LOCK_alarm);
    pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
    DBUG_RETURN(1);
  }
171 172 173
  if (alarm_aborted < 0)
    sec= 1;					/* Abort mode */

174
  if (alarm_queue.elements >= max_used_alarms)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
175
  {
176 177 178 179
    if (alarm_queue.elements == alarm_queue.max_elements)
    {
      DBUG_PRINT("info", ("alarm queue full"));
      fprintf(stderr,"Warning: thr_alarm queue is full\n");
180
      *alrm= 0;					/* No alarm */
181 182 183 184 185
      pthread_mutex_unlock(&LOCK_alarm);
      pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
      DBUG_RETURN(1);
    }
    max_used_alarms=alarm_queue.elements+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
186 187 188 189 190 191 192 193 194
  }
  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()"));
195
      *alrm= 0;					/* No alarm */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
196 197 198 199 200 201 202 203 204 205
      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;
206 207
  alarm_data->thread=    current_my_thread_var->pthread_self;
  alarm_data->thread_id= current_my_thread_var->id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  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);
}


/*
227
  Remove alarm from list of alarms
bk@work.mysql.com's avatar
bk@work.mysql.com committed
228 229 230 231 232 233
*/

void thr_end_alarm(thr_alarm_t *alarmed)
{
  ALARM *alarm_data;
  sigset_t old_mask;
234
  uint i, found=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
235 236 237 238 239 240 241 242 243 244 245 246 247
  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));
248
      found++;
249
#ifdef DBUG_OFF
bk@work.mysql.com's avatar
bk@work.mysql.com committed
250
      break;
251
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
252 253
    }
  }
254
  DBUG_ASSERT(!*alarmed || found == 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
255 256
  if (!found)
  {
257
    if (*alarmed)
258
      fprintf(stderr,"Warning: Didn't find alarm 0x%lx in queue of %d alarms\n",
259
	      (long) *alarmed, alarm_queue.elements);
260
    DBUG_PRINT("warning",("Didn't find alarm 0x%lx in queue\n",
261
			  (long) *alarmed));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
262
  }
263
  pthread_mutex_unlock(&LOCK_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
264 265 266 267
  pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
  DBUG_VOID_RETURN;
}

268 269 270 271 272 273 274
/*
  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.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
275 276 277 278

sig_handler process_alarm(int sig __attribute__((unused)))
{
  sigset_t old_mask;
279 280 281
/*
  This must be first as we can't call DBUG inside an alarm for a normal thread
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
282

283 284
  if (thd_lib_detected == THD_LIB_LT &&
      !pthread_equal(pthread_self(),alarm_thread))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
285 286
  {
#if defined(MAIN) && !defined(__bsdi__)
287
    printf("thread_alarm in process_alarm\n"); fflush(stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
288 289
#endif
#ifdef DONT_REMEMBER_SIGNAL
290
    my_sigset(thr_client_alarm, process_alarm);	/* int. thread system calls */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
291
#endif
292
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
293
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
294 295 296 297 298 299 300 301

  /*
    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
  */

bk@work.mysql.com's avatar
bk@work.mysql.com committed
302 303 304
#ifndef USE_ALARM_THREAD
  pthread_sigmask(SIG_SETMASK,&full_signal_set,&old_mask);
  pthread_mutex_lock(&LOCK_alarm);
305
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
306 307 308
  process_alarm_part2(sig);
#ifndef USE_ALARM_THREAD
#if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND)
309
  my_sigset(THR_SERVER_ALARM,process_alarm);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
310 311 312 313 314 315 316 317 318 319 320
#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;
321 322 323 324 325
  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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
326 327 328 329 330 331 332 333 334 335 336
#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) ||
337
	    pthread_kill(alarm_data->thread, thr_client_alarm))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	{
#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) ||
361
	    pthread_kill(alarm_data->thread, thr_client_alarm))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
	{
#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;
}


/*
392 393 394 395 396 397 398 399 400 401 402 403
  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
bk@work.mysql.com's avatar
bk@work.mysql.com committed
404 405
*/

406
void end_thr_alarm(my_bool free_structures)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
407 408
{
  DBUG_ENTER("end_thr_alarm");
409
  if (alarm_aborted != 1)			/* If memory not freed */
serg@serg.mylan's avatar
serg@serg.mylan committed
410
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
411
    pthread_mutex_lock(&LOCK_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
412
    DBUG_PRINT("info",("Resheduling %d waiting alarms",alarm_queue.elements));
413
    alarm_aborted= -1;				/* mark aborted */
414 415 416 417 418 419 420
    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();
    }
421 422
    if (free_structures)
    {
423
      struct timespec abstime;
serg@serg.mylan's avatar
serg@serg.mylan committed
424

425
      DBUG_ASSERT(!alarm_queue.elements);
426

serg@serg.mylan's avatar
serg@serg.mylan committed
427
      /* Wait until alarm thread dies */
428 429 430 431 432 433 434
      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 */
      }
serg@serg.mylan's avatar
serg@serg.mylan committed
435 436 437 438
      delete_queue(&alarm_queue);
      alarm_aborted= 1;
      pthread_mutex_unlock(&LOCK_alarm);
      if (!alarm_thread_running)              /* Safety */
439
      {
serg@serg.mylan's avatar
serg@serg.mylan committed
440 441
        pthread_mutex_destroy(&LOCK_alarm);
        pthread_cond_destroy(&COND_alarm);
442 443 444 445
      }
    }
    else
      pthread_mutex_unlock(&LOCK_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
446 447 448 449 450 451
  }
  DBUG_VOID_RETURN;
}


/*
452
  Remove another thread from the alarm
bk@work.mysql.com's avatar
bk@work.mysql.com committed
453 454
*/

455
void thr_alarm_kill(my_thread_id thread_id)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
456 457
{
  uint i;
458 459
  if (alarm_aborted)
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
460 461 462
  pthread_mutex_lock(&LOCK_alarm);
  for (i=0 ; i < alarm_queue.elements ; i++)
  {
463
    if (((ALARM*) queue_element(&alarm_queue,i))->thread_id == thread_id)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
464 465 466 467 468 469 470 471 472 473 474 475
    {
      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);
}


476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
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);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
492
/*
493 494
  This is here for thread to get interruptet from read/write/fcntl
  ARGSUSED
bk@work.mysql.com's avatar
bk@work.mysql.com committed
495 496
*/

497

bk@work.mysql.com's avatar
bk@work.mysql.com committed
498 499 500 501 502 503
static sig_handler thread_alarm(int sig)
{
#ifdef MAIN
  printf("thread_alarm\n"); fflush(stdout);
#endif
#ifdef DONT_REMEMBER_SIGNAL
504
  my_sigset(sig,thread_alarm);		/* int. thread system calls */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
#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();
525
  alarm_thread_running= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
  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
	}
      }
    }
550
    else if (alarm_aborted == -1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
551 552 553 554 555 556 557 558 559 560 561
      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 */
562 563
  alarm_thread_running= 0;
  pthread_cond_signal(&COND_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
564 565 566 567
  pthread_mutex_unlock(&LOCK_alarm);
  pthread_exit(0);
  return 0;					/* Impossible */
}
568 569 570
#endif /* USE_ALARM_THREAD */

/*****************************************************************************
571
  thr_alarm for win95
572 573 574 575
*****************************************************************************/

#else /* __WIN__ */

576
void thr_alarm_kill(my_thread_id thread_id)
577 578 579 580 581 582 583 584 585 586
{
  /* Can't do this yet */
}

sig_handler process_alarm(int sig __attribute__((unused)))
{
  /* Can't do this yet */
}


587
my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm)
588
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
589
  (*alrm)= &alarm->alarmed;
590 591
  if (alarm_aborted)
  {
592
    alarm->alarmed.crono=0;
593 594
    return 1;
  }
595 596
  if (!(alarm->alarmed.crono=SetTimer((HWND) NULL,0, sec*1000,
				      (TIMERPROC) NULL)))
597 598 599 600 601
    return 1;
  return 0;
}


602
bool thr_got_alarm(thr_alarm_t *alrm_ptr)
603
{
604
  thr_alarm_t alrm= *alrm_ptr;
605 606 607 608 609 610 611 612 613 614 615 616 617 618
  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;
}


619
void thr_end_alarm(thr_alarm_t *alrm_ptr)
620
{
621
  thr_alarm_t alrm= *alrm_ptr;
622
  /* alrm may be zero if thr_alarm aborted with an error */
vva@genie.(none)'s avatar
vva@genie.(none) committed
623
  if (alrm && alrm->crono)
624

625 626 627 628 629 630
  {
    KillTimer(NULL, alrm->crono);
    alrm->crono = 0;
  }
}

631
void end_thr_alarm(my_bool free_structures)
632 633 634 635 636 637
{
  DBUG_ENTER("end_thr_alarm");
  alarm_aborted=1;				/* No more alarms */
  DBUG_VOID_RETURN;
}

638 639 640 641 642 643 644
void init_thr_alarm(uint max_alarm)
{
  DBUG_ENTER("init_thr_alarm");
  alarm_aborted=0;				/* Yes, Gimmie alarms */
  DBUG_VOID_RETURN;
}

645 646 647 648 649
void thr_alarm_info(ALARM_INFO *info)
{
  bzero((char*) info, sizeof(*info));
}

650 651 652 653
void resize_thr_alarm(uint max_alarms)
{
}

654 655 656 657
#endif /* __WIN__ */

#endif /* THREAD */

bk@work.mysql.com's avatar
bk@work.mysql.com committed
658

659
/****************************************************************************
660
  Handling of test case (when compiled with -DMAIN)
661
***************************************************************************/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
662 663

#ifdef MAIN
664
#if defined(THREAD) && !defined(DONT_USE_THR_ALARM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
665 666 667 668 669

static pthread_cond_t COND_thread_count;
static pthread_mutex_t LOCK_thread_count;
static uint thread_count;

670
#ifdef HPUX10
bk@work.mysql.com's avatar
bk@work.mysql.com committed
671 672 673
typedef int * fd_set_ptr;
#else
typedef fd_set * fd_set_ptr;
674
#endif /* HPUX10 */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
675 676 677 678 679 680 681 682 683

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();
684
  printf("Thread %d (%s) started\n",param,my_thread_name()); fflush(stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
  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
    {
701
      for (retry=0 ; !thr_got_alarm(&got_alarm) && retry < 10 ; retry++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
702 703 704 705
      {
	printf("Thread: %s  Waiting %d sec\n",my_thread_name(),wait_time);
	select(0,(fd_set_ptr) &fd,0,0,0);
      }
706
      if (!thr_got_alarm(&got_alarm))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
      {
	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
768
  my_sigset(sig,print_signal_warning);		/* int. thread system calls */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
769 770 771 772
#endif
  if (sig == SIGALRM)
    alarm(2);					/* reschedule alarm */
}
773
#endif /* USE_ONE_SIGNAL_HAND */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802


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);

  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",
803
         THR_SERVER_ALARM, thr_client_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
  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:
    case SIGHUP:
      printf("Aborting nicely\n");
826
      end_thr_alarm(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
      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,error;
  sigset_t set;
850
  ALARM_INFO alarm_info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
851 852 853
  MY_INIT(argv[0]);

  if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
854
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
855
    DBUG_PUSH(argv[1]+2);
856
  }
857
  pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
858 859 860 861 862 863 864 865 866 867 868 869 870
  pthread_cond_init(&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);
871
  sigdelset(&set, thr_client_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
872 873 874
  (void) pthread_sigmask(SIG_SETMASK,&set,NULL);
#ifdef NOT_USED
  sigemptyset(&set);
875
  sigaddset(&set, thr_client_alarm);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
  VOID(pthread_sigmask(SIG_UNBLOCK, &set, (sigset_t*) 0));
#endif

  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);
910 911 912 913
  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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
914 915 916 917 918 919
  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");
920
      end_thr_alarm(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
921 922 923
    }
  }
  pthread_mutex_unlock(&LOCK_thread_count);
924
  thr_alarm_info(&alarm_info);
925
  end_thr_alarm(1);
926 927 928
  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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
929 930 931 932
  printf("Test succeeded\n");
  return 0;
}

933
#else /* THREAD */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
934 935 936

int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
{
937 938 939
#ifndef THREAD
  printf("thr_alarm disabled because we are not using threads\n");
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
940 941
  printf("thr_alarm disabled with DONT_USE_THR_ALARM\n");
#endif
942
  exit(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
943 944
}

945 946
#endif /* THREAD */
#endif /* MAIN */