Codebase list fis-gtm / HEAD sr_unix / gt_timers.c
HEAD

Tree @HEAD (Download .tar.gz)

gt_timers.c @HEADraw · history · blame

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  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
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 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
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 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
 768
 769
 770
 771
 772
 773
 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
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 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
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
/****************************************************************
 *								*
 * Copyright (c) 2001-2021 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

/* This file contains a general purpose timer package. Simultaneous multiple timers are supported.
 * All outstanding timers are contained in a queue of pending requests. New timer is added to the
 * queue in an expiration time order. The first timer in a queue expires first, and the last one
 * expires last. When the timer expires, the signal is generated and the process is awakened. This
 * timer is then removed from the queue, and the first timer in a queue is started again, and so on.
 * Starting a timer with the timer id equal to one of the existing timers in a chain will remove the
 * existing timer from the chain and add a new one instead.
 *
 * It is a responsibility of the user to go to hibernation mode by executing appropriate system call
 * if the user needs to wait for the timer expiration.
 *
 * Additionally, certain timers, designated by "safe" flag, can be processed---and, if necessary, out
 * of order---while we are deferred on interrupts. All regular timers that pop within the deferred
 * zone, will be handler in order as soon as we reenable interrupt processing.
 *
 * Following are top-level user-callable routines of this package:
 *
 * void sys_get_curr_time(ABS_TIME *atp)
 * 	fetch absolute time into stucture
 *
 * void hiber_start(uint4 hiber)
 *      used to sleep for hiber milliseconds
 *
 * void start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 dlen, char *data)
 *	Used to start a new timer.
 *
 * void cancel_timer(TID tid)
 *	Cancel an existing timer.
 *	Cancelling timer with tid = 0, cancels all timers.
 */

#include "mdef.h"

#include "gtm_signal.h"
#include "gtm_time.h"
#include "gtm_string.h"
#include "gtmimagename.h"

#include <errno.h>
#include <stddef.h>
#include <stdarg.h>

#if (defined(__ia64) && defined(__linux__)) || defined(__MVS__)
# include "gtm_unistd.h"
#endif /* __ia64 && __linux__ or __MVS__ */
#include "gt_timer.h"
#include "wake_alarm.h"
#ifdef DEBUG
# include "wbox_test_init.h"
# include "io.h"
#endif
#if	defined(mips) && !defined(_SYSTYPE_SVR4)
# include <bsd/sys/time.h>
#else
# include <sys/time.h>
#endif
#ifndef __MVS__
# include <sys/param.h>
#endif
#include "send_msg.h"
#include "gtmio.h"
#include "have_crit.h"
#include "util.h"
#include "sleep.h"
#include "error.h"
#include "gtm_multi_thread.h"
#include "gtmxc_types.h"

/*#define DEBUG_SIGSAFE*/
#ifdef DEBUG_SIGSAFE
# define DBGSIGSAFEFPF(x) DBGFPF(x)
/*#include "gtmio.h"*/
#include "io.h"
#else
# define DBGSIGSAFEFPF(x)
#endif

#ifdef ITIMER_REAL
# define USER_HZ 1000
#endif

#define TIMER_BLOCK_SIZE	64	/* # of timer entries allocated initially as well as at every expansion */
#define GT_TIMER_EXPAND_TRIGGER	8	/* if the # of timer entries in the free queue goes below this, allocate more */
#define MAX_TIMER_POP_TRACE_SZ	32

#define ADD_SAFE_HNDLR(HNDLR)									\
{												\
	assert((ARRAYSIZE(safe_handlers) - 1) > safe_handlers_cnt);				\
	assert(NULL != (void *)HNDLR); /* void * to avoid warnings of always true */		\
	safe_handlers[safe_handlers_cnt++] = HNDLR;						\
}

#define REPORT_SETITIMER_ERROR(TIMER_TYPE, SYS_TIMER, FATAL, ERRNO)				\
{												\
	char s[512];										\
												\
	SNPRINTF(s, 512, "Timer: %s; timer_active: %d; "					\
		"sys_timer.it_value: [tv_sec: %ld; tv_usec: %ld]; "				\
		"sys_timer.it_interval: [tv_sec: %ld; tv_usec: %ld]",				\
		TIMER_TYPE, timer_active,							\
		SYS_TIMER.it_value.tv_sec, SYS_TIMER.it_value.tv_usec,				\
		SYS_TIMER.it_interval.tv_sec, SYS_TIMER.it_interval.tv_usec);			\
	if (FATAL)										\
	{											\
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7)						\
			ERR_SETITIMERFAILED, 1, ERRNO, ERR_TEXT, 2, LEN_AND_STR(s));		\
		in_setitimer_error = TRUE;							\
		rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_SETITIMERFAILED, 1, ERRNO);	\
	} else											\
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(7)	MAKE_MSG_WARNING(ERR_SETITIMERFAILED),	\
			1, ERRNO, ERR_TEXT, 2, LEN_AND_STR(s));					\
}

#define SYS_SETTIMER(TIMER, DELTA)								\
MBSTART {											\
	sys_timer_at = (TIMER)->expir_time;							\
	sys_settimer((TIMER)->tid, DELTA);							\
} MBEND

STATICDEF struct itimerval	sys_timer, old_sys_timer;
STATICDEF ABS_TIME		sys_timer_at;			/* Absolute time associated with sys_timer */
STATICDEF boolean_t		in_setitimer_error;

#define DUMMY_SIG_NUM		0		/* following can be used to see why timer_handler was called */
#define SAFE_FOR_ANY_TIMER	((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (FALSE == process_exiting) && !fast_lock_count)
/* In case threads are running, we dont want any unsafe timers to be handled during a timer handler pop. This is because we
 * dont know if the threads will modify the same global variable that the unsafe timer modifies concurrently.
 * But it is okay for timers to be started by individual threads. For example the iott_flush_timer will be started inside
 * thread code only while holding a mutex lock (e.g. inside gtm_putmsg_list or so) and even though a "setitimer" call is done
 * inside one thread, the SIGALRM pop will happen only in the parent process because all threads have SIGALRM disabled in their
 * signal mask. Define SAFE_FOR_TIMER_POP and SAFE_FOR_TIMER_START variables accordingly.
 */
#define	SAFE_FOR_TIMER_POP	(SAFE_FOR_ANY_TIMER && !multi_thread_in_use)
#define	SAFE_FOR_TIMER_START	(SAFE_FOR_ANY_TIMER)

STATICDEF volatile GT_TIMER *timeroot = NULL;	/* chain of pending timer requests in time order */
STATICDEF boolean_t first_timeset = TRUE;
STATICDEF struct sigaction prev_alrm_handler;	/* save previous SIGALRM handler, if any */

/* Chain of unused timer request blocks */
STATICDEF volatile	GT_TIMER	*timefree = NULL;
STATICDEF volatile 	int4		num_timers_free;		/* # of timers in the unused queue */
STATICDEF volatile 	st_timer_alloc	*timer_allocs = NULL;

STATICDEF int 		safe_timer_cnt, timer_pop_cnt;			/* Number of safe timers in queue/popped */
STATICDEF TID 		*deferred_tids;

STATICDEF timer_hndlr	safe_handlers[MAX_SAFE_TIMER_HNDLRS + 1];	/* +1 for NULL to terminate list */
STATICDEF int		safe_handlers_cnt;

STATICDEF boolean_t	stolen_timer = FALSE;	/* only complain once, used in check_for_timer_pops() */
STATICDEF boolean_t	stopped_timer = FALSE;	/* only complain once, used in check_for_timer_pops() */
STATICDEF char 		*whenstolen[] = {"check_for_timer_pops", "check_for_timer_pops first time"}; /* for check_for_timer_pops */

#ifdef DEBUG
STATICDEF int		trc_timerpop_idx;
STATICDEF GT_TIMER	trc_timerpop_array[MAX_TIMER_POP_TRACE_SZ];

# define TRACE_TIMER_POP(TIMER_INFO)							\
{											\
	memcpy(&trc_timerpop_array[trc_timerpop_idx], TIMER_INFO, SIZEOF(GT_TIMER));	\
	trc_timerpop_idx = (trc_timerpop_idx + 1) % MAX_TIMER_POP_TRACE_SZ;		\
}
#endif

/* Get current clock time from the target clock when using clock_gettime()
 * Arguments:	atp - pointer to absolute structure of time
 * 		clockid - one of CLOCK_REALTIME or CLOCK_MONOTONIC
 */
#define	GET_TIME_CORE(ATP, CLOCKID)									\
MBSTART {												\
	struct timespec	ts;										\
													\
	/* Note: This code is called from timer_handler and so needs to be async-signal safe.		\
	 * POSIX defines "clock_gettime" as safe but not "gettimeofday" so dont use the latter.		\
	 */												\
	clock_gettime(CLOCKID, &ts);									\
	atp->at_sec = (int4)ts.tv_sec;									\
	atp->at_usec = (int4)ts.tv_nsec / 1000;								\
} MBEND

/* Sleep for MS milliseconds of "clockid" time unless interrupted by RESTART processing */
#ifdef _AIX
/* Because of unreliability, AIX uses plain old nanosleep() */
#define HIBER_START_CORE(MS, CLOCKID, RESTART)	SLEEP_USEC((MS * 1000), RESTART)
#else
#define HIBER_START_CORE(MS, CLOCKID, RESTART)				\
MBSTART {								\
	time_t	seconds, nanoseconds;					\
									\
	seconds = MS / 1000;						\
	nanoseconds = (MS % 1000) * E_6;				\
	CLOCK_NANOSLEEP(CLOCKID, seconds, nanoseconds, (RESTART));	\
} MBEND
#endif

/* Flag signifying timer is active. Especially useful when the timer handlers get nested. This has not been moved to a
 * threaded framework because we do not know how timers will be used with threads.
 */
GBLDEF	volatile boolean_t	timer_active = FALSE;
GBLDEF	volatile int4		timer_stack_count = 0;
GBLDEF	volatile boolean_t	timer_in_handler = FALSE;
GBLDEF	void			(*wcs_clean_dbsync_fptr)();	/* Reference to wcs_clean_dbsync() to be used in gt_timers.c. */
GBLDEF	void			(*wcs_stale_fptr)();		/* Reference to wcs_stale() to be used in gt_timers.c. */
GBLDEF 	boolean_t		deferred_timers_check_needed;	/* Indicator whether check_for_deferred_timers() should be called
								 * upon leaving deferred zone. */

GBLREF	boolean_t	blocksig_initialized;			/* Set to TRUE when blockalrm, block_ttinout, and block_sigsent are
								 * initialized. */
GBLREF	boolean_t	mu_reorg_process, oldjnlclose_started;
GBLREF	int		process_exiting;
GBLREF	int4		error_condition;
GBLREF	sigset_t	blockalrm;
GBLREF	sigset_t	block_sigsent;
GBLREF	sigset_t	block_ttinout;
GBLREF	sigset_t	block_worker;
GBLREF	void		(*jnl_file_close_timer_ptr)(void);	/* Initialized only in gtm_startup(). */
GBLREF	volatile int4	fast_lock_count, outofband;
#ifdef DEBUG
GBLREF	boolean_t	in_nondeferrable_signal_handler;
GBLREF	boolean_t	gtm_jvm_process;
#endif

error_def(ERR_SETITIMERFAILED);
error_def(ERR_TEXT);
error_def(ERR_TIMERHANDLER);

/* Preallocate some memory for timers. */
void gt_timers_alloc(void)
{
	int4		gt_timer_cnt;
	GT_TIMER	*timeblk, *timeblks;
	st_timer_alloc	*new_alloc;

	/* Allocate timer blocks putting each timer on the free queue */
	assert(1 > timer_stack_count);
#	ifdef DEBUG
	/* Allocate space for deferred timer tracking. We don't expect to need more than the initial TIMER_BLOCK_SIZE entries */
	if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS))
	{
		deferred_tids = (TID *)malloc(sizeof(TID) * TIMER_BLOCK_SIZE);
		memset((char *)deferred_tids, (char)0xff, sizeof(TID) * TIMER_BLOCK_SIZE);
	}
#	endif
	timeblk = timeblks = (GT_TIMER *)malloc((SIZEOF(GT_TIMER)) * TIMER_BLOCK_SIZE);
	new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc));
	new_alloc->addr = timeblk;
	new_alloc->next = (st_timer_alloc *)timer_allocs;
	timer_allocs = new_alloc;
	for (gt_timer_cnt = TIMER_BLOCK_SIZE; 0 < gt_timer_cnt; --gt_timer_cnt)
	{
		timeblk->hd_len_max = GT_TIMER_INIT_DATA_LEN;	/* Set amount it can store */
		timeblk->next = (GT_TIMER *)timefree;		/* Put on free queue */
		timefree = timeblk;
		timeblk = (GT_TIMER *)((char *)timeblk + SIZEOF(GT_TIMER));	/* Next! */
	}
	assert(((char *)timeblk - (char *)timeblks) == (SIZEOF(GT_TIMER)) * TIMER_BLOCK_SIZE);
	num_timers_free += TIMER_BLOCK_SIZE;
}

void add_safe_timer_handler(int safetmr_cnt, ...)
{
	int		i;
	va_list		var;
	timer_hndlr	tmrhndlr;

	VAR_START(var, safetmr_cnt);
	for (i = 1; i <= safetmr_cnt; i++)
	{
		tmrhndlr = va_arg(var, timer_hndlr);
		ADD_SAFE_HNDLR(tmrhndlr);
	}
	va_end(var);
}

/* Do the initialization of blockalrm, block_ttinout and block_sigsent, and set blocksig_initialized to TRUE, so
 * that we can later block signals when there is a need. This function should be called very early
 * in the main() routines of modules that wish to do their own interrupt handling.
 */
void set_blocksig(void)
{
	sigemptyset(&blockalrm);
	sigaddset(&blockalrm, SIGALRM);
	sigemptyset(&block_ttinout);
	sigaddset(&block_ttinout, SIGTTIN);
	sigaddset(&block_ttinout, SIGTTOU);
	sigemptyset(&block_sigsent);
	sigaddset(&block_sigsent, SIGINT);
	sigaddset(&block_sigsent, SIGQUIT);
	sigaddset(&block_sigsent, SIGTERM);
	sigaddset(&block_sigsent, SIGTSTP);
	sigaddset(&block_sigsent, SIGCONT);
	sigaddset(&block_sigsent, SIGALRM);
	sigfillset(&block_worker);
	sigdelset(&block_worker, SIGSEGV);
	sigdelset(&block_worker, SIGKILL);
	sigdelset(&block_worker, SIGFPE);
	sigdelset(&block_worker, SIGBUS);
	sigaddset(&block_worker, SIGTERM);
	blocksig_initialized = TRUE;	/* note the fact that blockalrm and block_sigsent are initialized */
}

/* Initialize group of timer blocks */
void prealloc_gt_timers(void)
{	/* Preallocate some timer blocks. This will be all the timer blocks we hope to need.
	 * Allocate them with 8 bytes of possible data each.
	 * If more timer blocks are needed, we will allocate them as needed.
	 */
	gt_timers_alloc();
	/* Now initialize the safe timers. Must be done dynamically to avoid the situation where this module always references all
	 * possible safe timers thus pulling extra stuff into executables that don't need or want it.
	 *
	 * First step, fill in the safe timers contained within this module which are always available.
	 * NOTE: Use gt_timers_add_safe_hndlrs to add safe timer handlers not visible to gtmsecshr
	 */
	ADD_SAFE_HNDLR(&wake_alarm);		/* Standalone module containing only one global reference */
}

/* Get current monotonic clock time. Fill-in the structure with the monotonic time of system clock */
void sys_get_curr_time(ABS_TIME *atp)
{
	GET_TIME_CORE(atp, CLOCK_MONOTONIC);
}

/* Get current "wall" clock time. Fill-in the structure with the absolute time of system clock.
 * WARNING: only op_hang uses this to ensure the HANG duration matches $[Z]Horlog/$ZUT time
 */
void sys_get_wall_time(ABS_TIME *atp)
{
	GET_TIME_CORE(atp, CLOCK_REALTIME);
}

/* Sleep for milliseconds of monotonic time unless interrupted by out-of-band processing */
void hiber_start(uint4 milliseconds)
{
	/* WARNING: negation of outofband is intentional */
	HIBER_START_CORE(milliseconds, CLOCK_MONOTONIC, !outofband);
}

/* Sleep for milliseconds of "wall clock" time unless interrupted by out-of-band processing
 * WARNING: only op_hang uses this to ensure the HANG duration matches $[Z]Horlog/$ZUT time
 */
void hiber_start_wall_time(uint4 milliseconds)
{
	/* WARNING: negation of outofband is intentional */
	HIBER_START_CORE(milliseconds, CLOCK_REALTIME, !outofband);
}

/* Sleep for milliseconds of time unless EINTRrupted */
void hiber_start_wait_any(uint4 milliseconds)
{
	HIBER_START_CORE(milliseconds, CLOCK_MONOTONIC, FALSE);
}

/* Wrapper function for start_timer() that is exposed for outside use. The function ensures that time_to_expir is positive. If
 * negative value or 0 is passed, set time_to_expir to 0 and invoke start_timer(). The reason we have not merged this functionality
 * with start_timer() is because there is no easy way to determine whether the function is invoked from inside GT.M or by an
 * external routine.
 * Arguments:	tid 		- timer id
 *		time_to_expir	- time to expiration in msecs
 *		handler		- pointer to handler routine
 *		hdata_len       - length of handler data next arg
 *		hdata		- data to pass to handler (if any)
 */
void gtm_start_timer(TID tid,
		 int4 time_to_expir,
		 void (*handler)(),
		 int4 hdata_len,
		 void *hdata)
{
	if (0 >= time_to_expir)
		time_to_expir = 0;
	start_timer(tid, time_to_expir, handler, hdata_len, hdata);
}

/* Start the timer. If timer chain is empty or this is the first timer to expire, actually start the system timer.
 * Arguments:	tid 		- timer id
 *		time_to_expir	- time to expiration in msecs
 *		handler		- pointer to handler routine
 *      	hdata_len       - length of handler data next arg
 *      	hdata		- data to pass to handler (if any)
 */
void start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata)
{
	sigset_t		savemask;
	boolean_t		safe_timer = FALSE, safe_to_add = FALSE;
	int			i, rc;
#ifdef DEBUG
	struct itimerval	curtimer;
#endif

	assertpro(0 <= time_to_expir);			/* Callers should verify non-zero time */
	DUMP_TIMER_INFO("At the start of start_timer()");
	if (NULL == handler)
	{
		safe_to_add = TRUE;
		safe_timer = TRUE;
	} else if (wcs_clean_dbsync_fptr == handler)
	{	/* Account for known instances of the above function being called from within a deferred zone. */
		assert((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) || (INTRPT_IN_WCS_WTSTART == intrpt_ok_state)
			|| (INTRPT_IN_GDS_RUNDOWN == intrpt_ok_state) || (INTRPT_IN_DB_CSH_GETN == intrpt_ok_state)
			|| (mu_reorg_process && (INTRPT_IN_KILL_CLEANUP == intrpt_ok_state)));
		safe_to_add = TRUE;
	} else if (wcs_stale_fptr == handler)
	{	/* Account for known instances of the above function being called from within a deferred zone. */
		assert((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) || (INTRPT_IN_DB_CSH_GETN == intrpt_ok_state)
			|| (INTRPT_IN_TRIGGER_NOMANS_LAND == intrpt_ok_state)
			|| (mu_reorg_process && (INTRPT_IN_KILL_CLEANUP == intrpt_ok_state)));
		safe_to_add = TRUE;
	} else
	{
		for (i = 0; NULL != safe_handlers[i]; i++)
		{
			if (safe_handlers[i] == handler)
			{
				safe_to_add = TRUE;
				safe_timer = TRUE;
				break;
			}
		}
	}
	if (!safe_to_add && !SAFE_FOR_TIMER_START)
	{
		assert(FALSE);
		return;
	}
	if (1 > timer_stack_count)
	{
		SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);	/* block SIGALRM signal */
#ifdef DEBUG
		if (TRUE == timer_active)	/* There had better be an active timer */
			assert(0 == getitimer(ITIMER_REAL, &curtimer));
#endif
	}
	start_timer_int(tid, time_to_expir, handler, hdata_len, hdata, safe_timer);
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);		/* reset signal handlers */
	DUMP_TIMER_INFO("At the end of start_timer()");
}

/* Internal version of start_timer that does not protect itself, assuming this has already been done.
 * Otherwise does as explained above in start_timer.
 */
STATICFNDEF void start_timer_int(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata, boolean_t safe_timer)
{
	ABS_TIME	at;
	GT_TIMER 	*newt;

	assert(0 <= time_to_expir);
	/* there is abuse of this api - hdata is a pointer, so hd_len, if supplied, should be the size of a pointer, but callers
	 * sometimes use the size of the pointed-to data
	 */
	sys_get_curr_time(&at);
	if (first_timeset)
	{
		init_timers();
		first_timeset = FALSE;
	}
	/* We expect no timer with id=<tid> to exist in the timer queue currently. This is asserted in "add_timer" call below.
	 * In pro though, we'll be safe and remove any tids that exist before adding a new entry with the same tid - 2009/10.
	 * If a few years pass without the assert failing, it might be safe then to remove the PRO_ONLY code below.
	 */
#	ifndef DEBUG
	remove_timer(tid); /* Remove timer from chain */
#	endif
	/* Check if # of free timer slots is less than minimum threshold. If so, allocate more of those while it is safe to do so */
	if ((GT_TIMER_EXPAND_TRIGGER > num_timers_free) && (1 > timer_stack_count))
		gt_timers_alloc();
	DUMP_TIMER_INFO("Before invoking add_timer()");
	newt = add_timer(&at, tid, time_to_expir, handler, hdata_len, hdata, safe_timer);	/* Put new timer in the queue. */
	DUMP_TIMER_INFO("After invoking add_timer()");
	if ((timeroot->tid == tid) || !timer_active
			|| (timer_active
				&& ((newt->expir_time.at_sec < sys_timer_at.at_sec)
					|| ((newt->expir_time.at_sec == sys_timer_at.at_sec)
						&& ((gtm_tv_usec_t)newt->expir_time.at_usec < sys_timer_at.at_usec)))))
		start_first_timer(&at);
}

/* Cancel timer.
 * Arguments:	tid - timer id
 */
void cancel_timer(TID tid)
{
	ABS_TIME	at;
	sigset_t	savemask;
	boolean_t	first_timer;
	int		rc;

	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);	/* block SIGALRM signal */
	DUMP_TIMER_INFO("At the start of cancel_timer()");
	sys_get_curr_time(&at);
	first_timer = (timeroot && (timeroot->tid == tid));
	remove_timer(tid);		/* remove it from the chain */
	if (first_timer)
	{
		if (timeroot)
			start_first_timer(&at);		/* start the first timer in the chain */
		else if (timer_active)
			sys_canc_timer();
	}
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);
	DUMP_TIMER_INFO("At the end of cancel_timer()");
}

/* Clear the timers' state for the forked-off process. */
void clear_timers(void)
{
	sigset_t	savemask;
	int		rc;

	DUMP_TIMER_INFO("At the start of clear_timers()");
	if (NULL == timeroot)
	{	/* If no timers have been initialized in this process, take fast path (avoid system call) */
		/* If the only timer popped, and we got a SIGTERM while its handler was active, the timeroot
		 * would be NULL and timer_in_handler would be TRUE, but that should be safe for the fast path,
		 * so allow this case if the process is exiting.
		 */
		assert((FALSE == timer_in_handler) || process_exiting);
		assert(FALSE == timer_active);
		assert(FALSE == oldjnlclose_started);
		assert(FALSE == deferred_timers_check_needed);
		return;
	}
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);	/* block SIGALRM signal */
	while (timeroot)
		remove_timer(timeroot->tid);
	timer_in_handler = FALSE;
	timer_active = FALSE;
	oldjnlclose_started = FALSE;
	deferred_timers_check_needed = FALSE;
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);
	DUMP_TIMER_INFO("After invoking clear_timers()");
	return;
}

/* System call to set timer. Time is given im msecs.
 * Arguments:	tid		- timer id
 *		time_to_expir	- time to expiration
 */
STATICFNDEF void sys_settimer(TID tid, ABS_TIME *time_to_expir)
{
	if (in_setitimer_error)
		return;
	sys_timer.it_value.tv_sec = time_to_expir->at_sec;
	sys_timer.it_value.tv_usec = (gtm_tv_usec_t)time_to_expir->at_usec;
	sys_timer.it_interval.tv_sec = sys_timer.it_interval.tv_usec = 0;
	assert(1000000 > sys_timer.it_value.tv_usec);
	if ((-1 == setitimer(ITIMER_REAL, &sys_timer, &old_sys_timer)) || WBTEST_ENABLED(WBTEST_SETITIMER_ERROR))
	{
		REPORT_SETITIMER_ERROR("ITIMER_REAL", sys_timer, TRUE, errno);
	}
#	ifdef TIMER_DEBUGGING
	FPRINTF(stderr, "------------------------------------------------------\n"
		"SETITIMER\n---------------------------------\n");
	FPRINTF(stderr, "System timer :\n  expir_time: [at_sec: %ld; at_usec: %ld]\n",
		sys_timer.it_value.tv_sec, sys_timer.it_value.tv_usec);
	FPRINTF(stderr, "Old System timer :\n  expir_time: [at_sec: %ld; at_usec: %ld]\n",
		old_sys_timer.it_value.tv_sec, old_sys_timer.it_value.tv_usec);
	FFLUSH(stderr);
#	endif
	timer_active = TRUE;
}

/* Start the first timer in the timer chain
 * Arguments:	curr_time	- current time assumed within the function
 */
STATICFNDEF void start_first_timer(ABS_TIME *curr_time)
{
	ABS_TIME	eltime;
	GT_TIMER	*tpop;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	DUMP_TIMER_INFO("At the start of start_first_timer()");
	deferred_timers_check_needed = FALSE;
	if ((1 < timer_stack_count) || (TRUE == timer_in_handler))
		return;
	for (tpop = (GT_TIMER *)timeroot ; tpop ; tpop = tpop->next)
	{
		eltime = sub_abs_time((ABS_TIME *)&tpop->expir_time, curr_time);
		if ((0 > eltime.at_sec) || ((0 == eltime.at_sec) && (0 == eltime.at_usec)))
		{	/* Timer has expired. Handle safe timers, defer unsafe timers. */
			if (tpop->safe || (SAFE_FOR_TIMER_START && (1 > timer_stack_count)
						&& !(TREF(in_ext_call) && (wcs_stale_fptr == tpop->handler))))
			{
				timer_handler(DUMMY_SIG_NUM);
				/* At this point all timers should have been handled, including a recursive call to
				 * start_first_timer(), if needed, and deferred_timers_check_needed set to the appropriate
				 * value, so we are done.
				 */
				break;
			} else
			{
				deferred_timers_check_needed = TRUE;
				tpop->block_int = intrpt_ok_state;
			}
		} else
		{	/* Set system timer to wake on unexpired timer. */
			SYS_SETTIMER(tpop, &eltime);
			break;	/* System timer will handle subsequent timers, so we are done. */
		}
		assert(deferred_timers_check_needed);
	}
	assert(timeroot || !deferred_timers_check_needed);
	DUMP_TIMER_INFO("At the end of start_first_timer()");
}

/* Timer handler. This is the main handler routine that is being called by the kernel upon receipt
 * of timer signal. It dispatches to the user handler routine, and removes first timer in a timer
 * queue. If the queue is not empty, it starts the first timer in the queue. The why parameter is a
 * no-op in our case, but is required to maintain compatibility with the system type of __sighandler_t,
 * which is (void*)(int).
 */
STATICFNDEF void timer_handler(int why)
{
	int4		cmp, save_error_condition;
	GT_TIMER	*tpop, *tpop_prev = NULL;
	ABS_TIME	at;
	int		save_errno, timer_defer_cnt;
	TID 		*deferred_tid;
	boolean_t	tid_found;
	char 		*save_util_outptr;
	va_list		save_last_va_list_ptr;
	boolean_t	util_copy_saved = FALSE, safe_for_timer_pop;
#	ifdef DEBUG
	boolean_t	save_in_nondeferrable_signal_handler;
	ABS_TIME	rel_time, old_at, late_time;
	static int	last_continue_proc_cnt = -1;
#	endif
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(gtm_is_main_thread() || gtm_jvm_process);
	DUMP_TIMER_INFO("At the start of timer_handler()");
	if (SIGALRM == why)
	{	/* If why is 0, we know that timer_handler() was called directly, so no need
		 * to check if the signal needs to be forwarded to appropriate thread.
		 */
		FORWARD_SIG_TO_MAIN_THREAD_IF_NEEDED(SIGALRM);
	}
#	ifdef DEBUG
	/* Note that it is possible "in_nondeferrable_signal_handler" is non-zero if we first went into generic_signal_handler
	 * (say to handle sig-3) and then had a timer handler pop while inside there (possible for example in receiver server).
	 * So save current value of global and restore it at end of this function.
	 */
	save_in_nondeferrable_signal_handler = in_nondeferrable_signal_handler;
#	endif
	/* timer_handler() may or may not be protected from signals.
	 * If why is SIGALRM, the OS typically blocks SIGALRM while this handler is executing.
	 * If why is DUMMY_SIG_NUM, SIGALRM is not blocked, so make sure that a concurrent SIGALRM bails out at this point.
	 * All other routines which manipulate the timer data structure block SIGALRM (using SIGPROCMASK), so timer_handler()
	 * can't conflict with them. As long as those routines can't be invoked asynchronously while timer_handler (or another
	 * of those routines) is running, there can be no conflict, and the timer structures are safe from concurrent manipulation.
	 */
	if (1 < INTERLOCK_ADD(&timer_stack_count, UNUSED, 1))
	{
		deferred_timers_check_needed = TRUE;
		INTERLOCK_ADD(&timer_stack_count, UNUSED, -1);
		return;
	}
	deferred_timers_check_needed = FALSE;
	save_errno = errno;
	save_error_condition = error_condition;	/* aka SIGNAL */
	timer_active = FALSE;				/* timer has popped; system timer not active anymore */
	sys_get_curr_time(&at);
	tpop = (GT_TIMER *)timeroot;
	timer_defer_cnt = 0;				/* reset the deferred timer count, since we are in timer_handler */
	safe_for_timer_pop = SAFE_FOR_TIMER_POP;
	/* If "multi_thread_in_use" is TRUE, it is possible util_out* buffers are concurrently being manipulated by the running
	 * threads. So do not use SAVE/RESTORE_UTIL_OUT_BUFFER macros. Thankfully in this case, "safe_for_timer_pop" will
	 * be FALSE (asserted below) and so only safe timer handlers will be driven. We expect the safe timer handlers to
	 * not play with the util_out* buffers. So it is actually okay to not do the SAVE/RESTORE_UTIL_OUT_BUFFER.
	 */
	assert(!multi_thread_in_use || !safe_for_timer_pop);
	if (safe_for_timer_pop)
		SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved);
#	ifdef DEBUG
	if (safe_for_timer_pop)
		in_nondeferrable_signal_handler = IN_TIMER_HANDLER;
	/* Allow a base 50 seconds of lateness for safe timers */
	late_time.at_sec = 50;
	late_time.at_usec = 0;
#	endif
	while (tpop)					/* fire all handlers that expired */
	{
		cmp = abs_time_comp(&at, (ABS_TIME *)&tpop->expir_time);
		if (cmp < 0)
			break;
#		if defined(DEBUG) && !defined(_AIX)
		if (tpop->safe && (TREF(continue_proc_cnt) == last_continue_proc_cnt)
			&& !(gtm_white_box_test_case_enabled
				&& ((WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP == gtm_white_box_test_case_number)
				|| (WBTEST_EXPECT_IO_HANG == gtm_white_box_test_case_number)
				|| (WBTEST_OINTEG_WAIT_ON_START == gtm_white_box_test_case_number))))
		{	/* Check if the timer is extremely overdue, with the following exceptions:
			 *	- Unsafe timers can be delayed indefinitely.
			 *	- AIX systems tend to arbitrarily delay processes when loaded.
			 *	- WBTEST_SIGTSTP_IN_JNL_OUTPUT_SP stops the process from running.
			 *	- Some other mechanism causes a SIGSTOP/SIGCONT, bumping continue_proc_cnt.
			 */
			rel_time = sub_abs_time(&at, (ABS_TIME *)&tpop->expir_time);
			if (abs_time_comp(&late_time, &rel_time) <= 0)
				gtm_fork_n_core();	/* Dump core, but keep going. */
		}
		last_continue_proc_cnt = TREF(continue_proc_cnt);
#		endif
		/* A timer might pop while we are in the non-zero intrpt_ok_state zone, which could cause collisions. Instead,
		 * we will defer timer events and drive them once the deferral is removed, unless the timer is safe.
		 * Handle wcs_stale timers during external calls similarly.
		 */
		if ((safe_for_timer_pop && !(TREF(in_ext_call) && (wcs_stale_fptr == tpop->handler))) || tpop->safe)
		{
			if (NULL != tpop_prev)
				tpop_prev->next = tpop->next;
			else
				timeroot = tpop->next;
			if (tpop->safe)
			{
				safe_timer_cnt--;
				assert(0 <= safe_timer_cnt);
			}
			if (NULL != tpop->handler)	/* if there is a handler, call it */
			{
#				ifdef DEBUG
				if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS)
					&& ((void *)tpop->handler != (void*)jnl_file_close_timer_ptr))
				{
					DBGFPF((stderr, "TIMER_HANDLER: handled a timer\n"));
					timer_pop_cnt++;
				}
#				endif
				timer_in_handler = TRUE;
				(*tpop->handler)(tpop->tid, tpop->hd_len, tpop->hd_data);
				timer_in_handler = FALSE;
				if (!tpop->safe)		/* if safe, avoid a system call */
				{
					DEBUG_ONLY(old_at = at);
					sys_get_curr_time(&at);	/* refresh current time if called a handler */
#					ifdef DEBUG
					/* Include the time it took to handle the unsafe timer in the allowed late time.
					 * Otherwise, a hung unsafe timer could cause a subsequent safe timer to be overdue.
					 */
					rel_time = sub_abs_time(&at, &old_at);
					late_time.at_sec += rel_time.at_sec;
					late_time.at_usec += rel_time.at_usec;
					if (late_time.at_usec > MICROSECS_IN_SEC)
					{
						late_time.at_sec++;
						late_time.at_usec -= MICROSECS_IN_SEC;
					}
#					endif
				}
				DEBUG_ONLY(TRACE_TIMER_POP(tpop));
			}
			tpop->next = (GT_TIMER *)timefree;	/* put timer block on the free chain */
			timefree = tpop;
			if (NULL != tpop_prev)
				tpop = tpop_prev->next;
			else
				tpop = (GT_TIMER *)timeroot;
			num_timers_free++;
			assert(0 < num_timers_free);
		} else
		{
			timer_defer_cnt++;
#			ifdef DEBUG
			if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS))
			{
				tid_found = FALSE;
				deferred_tid = deferred_tids;
				while (-1 != *deferred_tid)
				{
					if (*deferred_tid == tpop->tid)
					{
						tid_found = TRUE;
						break;
					}
					deferred_tid++;
					/* WBTEST_DEFERRED_TIMERS tests do not need more than TIMER_BLOCK_SIZE entries, right? */
					assert(TIMER_BLOCK_SIZE >= (deferred_tid - deferred_tids));
				}
				if (!tid_found)
				{
					*deferred_tid = tpop->tid;
					DBGFPF((stderr, "TIMER_HANDLER: deferred a timer\n"));
				}
			}
#			endif
			tpop->block_int = intrpt_ok_state;
			tpop_prev = tpop;
			tpop = tpop->next;
			if ((0 == safe_timer_cnt) && !(TREF(in_ext_call) && (wcs_stale_fptr == tpop_prev->handler)))
				break;		/* no more safe timers left, and not special case, so quit */
		}
	}
	if (safe_for_timer_pop)
		RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved);
	if (safe_for_timer_pop || (0 < safe_timer_cnt))
		start_first_timer(&at);
	else if ((NULL != timeroot) || (0 < timer_defer_cnt))
		deferred_timers_check_needed = TRUE;
	/* Restore mainline error_condition global variable. This way any gtm_putmsg or rts_errors that occurred inside interrupt
	 * code do not affect the error_condition global variable that mainline code was relying on. For example, not doing this
	 * restore caused the update process (in updproc_ch) to issue a GTMASSERT (GTM-7526). BYPASSOK.
	 */
	SET_ERROR_CONDITION(save_error_condition);	/* restore error_condition & severity */
	errno = save_errno;			/* restore mainline errno by similar reasoning as mainline error_condition */
	INTERLOCK_ADD(&timer_stack_count, UNUSED, -1);
#	ifdef DEBUG
	if (safe_for_timer_pop)
		in_nondeferrable_signal_handler = save_in_nondeferrable_signal_handler;
#	endif
	DUMP_TIMER_INFO("At the end of timer_handler()");
}

/* Find a timer given by tid in the timer chain.
 * Arguments:	tid	- timer id
 *		tprev	- address of pointer to previous node
 * Return:	pointer to timer in the chain, or 0 if timer is not found
 * Note:	tprev is set to the link previous to the tid link
 */
STATICFNDEF GT_TIMER *find_timer(TID tid, GT_TIMER **tprev)
{
	GT_TIMER *tc;

	tc = (GT_TIMER *)timeroot;
	*tprev = NULL;
	while (tc)
	{
		if (tc->tid == tid)
			return tc;
		*tprev = tc;
		tc = tc->next;
	}
	return 0;
}

/* Add timer to timer chain. Allocate a new link for a timer. Convert time to expiration into absolute time.
 * Insert new link into chain in timer order.
 * Arguments:	tid		- timer id
 *		time_to_expir	- elapsed time to expiration
 *		handler		- pointer to handler routine
 *      	hdata_len       - length of data to follow
 *      	hdata   	- data to pass to timer rtn if any
 *      	safe_timer	- timer's handler is in safe_handlers array
 */
STATICFNDEF GT_TIMER *add_timer(ABS_TIME *atp, TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len,
	void *hdata, boolean_t safe_timer)
{
	GT_TIMER	*tp, *tpp, *ntp, *lastntp;
	int4		cmp, i;
	st_timer_alloc	*new_alloc;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* assert that no timer entry with the same "tid" exists in the timer chain */
	assert(NULL == find_timer(tid, &tpp));
	assert(GT_TIMER_INIT_DATA_LEN >= hdata_len);
	/* obtain a new timer block */
	ntp = (GT_TIMER *)timefree;
	lastntp = NULL;
	for ( ; NULL != ntp; )
	{	/* we expect all callers of timer functions to not require more than 8 bytes of data; any violations
		 * of this assumption need to be caught---hence the assert below
		 */
		assert(GT_TIMER_INIT_DATA_LEN == ntp->hd_len_max);
		assert(ntp->hd_len_max >= hdata_len);
		if (ntp->hd_len_max >= hdata_len)	/* found one that can hold our data */
		{	/* dequeue block */
			if (NULL == lastntp)		/* first one on queue */
				timefree = ntp->next;	/* dequeue 1st element */
			else				/* is not 1st on queue -- use simple dequeue */
				lastntp->next = ntp->next;
			assert(0 < num_timers_free);
			num_timers_free--;
			break;
		}
		lastntp = ntp;	/* still looking, try next block */
		ntp = ntp->next;
	}
	/* if didn't find one, fail if dbg; else malloc a new one */
	if (NULL == ntp)
	{
		assert(FALSE);							/* if dbg, we should have enough already */
		ntp = (GT_TIMER *)malloc(SIZEOF(GT_TIMER));			/* if we are in a timer, malloc may error out */
		new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc));	/* insert in front of the list */
		new_alloc->addr = ntp;
		new_alloc->next = (st_timer_alloc *)timer_allocs;
		timer_allocs = new_alloc;
		assert(GT_TIMER_INIT_DATA_LEN == hdata_len);
		ntp->hd_len_max = hdata_len;
	}
	ntp->tid = tid;
	ntp->handler = handler;
	if (safe_timer)
	{
		ntp->safe = TRUE;
		safe_timer_cnt++;
		assert(0 < safe_timer_cnt);
	} else
		ntp->safe = FALSE;
	ntp->block_int = INTRPT_OK_TO_INTERRUPT;
	ntp->hd_len = hdata_len;
	if (0 < hdata_len)
	{
		assert(GT_TIMER_INIT_DATA_LEN >= hdata_len);
		memcpy(ntp->hd_data, hdata, hdata_len);
	}
	add_int_to_abs_time(atp, time_to_expir, &ntp->expir_time);
	ntp->start_time.at_sec = atp->at_sec;
	ntp->start_time.at_usec = atp->at_usec;
	tp = (GT_TIMER *)timeroot;
	tpp = NULL;
	while (tp)
	{
		cmp = abs_time_comp(&tp->expir_time, &ntp->expir_time);
		if (cmp >= 0)
			break;
		tpp = tp;
		tp = tp->next;
	}
	ntp->next = tp;
	if (NULL == tpp)
		timeroot = ntp;
	else
		tpp->next = ntp;
	return ntp;
}

/* Remove timer from the timer chain. */
STATICFNDEF void remove_timer(TID tid)
{
	GT_TIMER *tprev, *tp, *tpp;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	DUMP_TIMER_INFO("At the start of remove_timer()");
	if (tp = find_timer(tid, &tprev))		/* Warning: assignment */
	{
		if (tprev)
			tprev->next = tp->next;
		else
		{
			timeroot = tp->next;
			if (NULL == timeroot)
				deferred_timers_check_needed = FALSE;	/* assert in fast path of "clear_timers" relies on this */
		}
		if (tp->safe)
			safe_timer_cnt--;
		tp->next = (GT_TIMER *)timefree;	/* place element on free queue */
		timefree = tp;
		num_timers_free++;
		assert(0 < num_timers_free);
		/* assert that no duplicate timer entry with the same "tid" exists in the timer chain */
		assert((NULL == find_timer(tid, &tpp)));
	}
	DUMP_TIMER_INFO("After invoking remove_timer()");
}

/* System call to cancel timer. Not static because can be called from generic_signal_handler() to stop timers
 * from popping yet preserve the blocks so gtmpcat can pick them out of the core. Note that once we exit,
 * timers are cleared at the top of the exit handler.
 */
void sys_canc_timer()
{
	struct itimerval zero;

	memset(&zero, 0, SIZEOF(struct itimerval));
	assert(timer_active);
	/* In case of canceling the system timer, we do not care if we succeed. Consider the two scenarios:
	 *   1) The process is exiting, so all timers must have been removed anyway, and regardless of whether the system
	 *      timer got unset or not, no handlers would be processed (even in the event of a pop).
	 *   2) Some timer is being canceled as part of the runtime logic. If the system is experiencing problems, then the
	 *      following attempt to schedule a new timer (remember that we at the very least have the heartbeat timer once
	 *      database access has been established) would fail; if no other timer is scheduled, then the canceled entry
	 *      must have been removed off the queue anyway, so no processing would occur on a pop.
	 */
	if (-1 == setitimer(ITIMER_REAL, &zero, &old_sys_timer))
	{
		REPORT_SETITIMER_ERROR("ITIMER_REAL", zero, FALSE, errno);
	}
	timer_active = FALSE;		/* no timer is active now */
}

/* Cancel all unsafe timers. */
void cancel_unsafe_timers(void)
{
	ABS_TIME	at;
	sigset_t	savemask;
	GT_TIMER	*active, *curr, *next;
	int		rc;
	DEBUG_ONLY(int4	cnt = 0;)
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	DUMP_TIMER_INFO("At the start of cancel_unsafe_timers()");
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);	/* block SIGALRM signal */
	active = curr = (GT_TIMER *)timeroot;
	while (curr)
	{	/* If the timer is unsafe, remove it from the chain. */
		next = curr->next;
		if (!curr->safe)
			remove_timer(curr->tid);
		curr = next;
		DEBUG_ONLY(cnt++;)
	}
	assert((NULL == timeroot) || (0 < safe_timer_cnt));
	if (timeroot)
	{	/* If the head of the queue has changed, or the system timer was not running, start the current first timer. */
		if ((active != timeroot) || (!timer_active))
		{
			sys_get_curr_time(&at);
			start_first_timer(&at);
		}
	} else
	{
		deferred_timers_check_needed = FALSE;
		/* There are no timers left, but the system timer was active, so cancel it. */
		if (timer_active)
			sys_canc_timer();
	}
#	ifdef DEBUG
	if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS))
	{
		DBGFPF((stderr, "CANCEL_ALL_TIMERS:\n"));
		DBGFPF((stderr, " Timer pops handled: %d\n", timer_pop_cnt));
		DBGFPF((stderr, " Timers canceled: %d\n", cnt));
	}
#	endif
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);
	DUMP_TIMER_INFO("After invoking cancel_unsafe_timers()");
}

/* Initialize timers. */
STATICFNDEF void init_timers()
{
	struct sigaction	act;

	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = timer_handler;
	sigaction(SIGALRM, &act, &prev_alrm_handler);
	if (first_timeset && 					/* not from timer_handler to prevent dup message */
	    (SIG_IGN != prev_alrm_handler.sa_handler) &&	/* as set by sig_init */
	    (SIG_DFL != prev_alrm_handler.sa_handler)) 		/* utils, compile */
	{
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler,
			LEN_AND_LIT("init_timers"));
		rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler,
			LEN_AND_LIT("init_timers"));
		assert(FALSE);
	}
}

/* Check for deferred timers. Drive any timers that have been deferred. In case the system timer is
 * disabled, launch it for the next scheduled event. This function should be called upon leaving the
 * interrupt-deferred zone.
 */
void check_for_deferred_timers(void)
{
	sigset_t	savemask;
	int		rc;
	char		*rname;

	assert(!INSIDE_THREADED_CODE(rname));	/* below code is not thread safe as it does SIGPROCMASK() etc. */
	deferred_timers_check_needed = FALSE;
	timer_handler(DUMMY_SIG_NUM);
}

/* Check for timer pops. If any timers are on the queue, pretend a sigalrm occurred, and we have to
 * check everything. This is mainly for use after external calls until such time as external calls
 * can use this timing facility. Current problem is that external calls are doing their own catching
 * of sigalarms that should be ours, so we end up hung.
 */
void check_for_timer_pops(boolean_t sig_handler_changed)
{
	int			rc, stolenwhen = 0;		/* 0 = no, 1 = not first, 2 = first time */
	sigset_t 		savemask;
	struct sigaction 	current_sa;
	int			save_errno = 0;

	DBGSIGSAFEFPF((stderr, "check_for_timer_pops: sig_handler_changed=%d, first_timeset=%d, timer_active=%d\n",
				sig_handler_changed, first_timeset, timer_active));
	if (sig_handler_changed)
	{
		sigaction(SIGALRM, NULL, &current_sa);	/* get current info */
		DBGSIGSAFEFPF((stderr, "check_for_timer_pops: current_sa.sa_handler=%p\n", current_sa.sa_handler));
		if (!first_timeset)
		{
			if (timer_handler != current_sa.sa_handler)	/* check if what we expected */
			{
				init_timers();
				if (!stolen_timer)
				{
					stolen_timer = TRUE;
					stolenwhen = 1;
				}
			}
		} else	/* we haven't set so should be ... */
		{
			if ((SIG_IGN != current_sa.sa_handler) &&	/* as set by sig_init */
			    (SIG_DFL != current_sa.sa_handler)) 	/* utils, compile */
			{
				if (!stolen_timer)
				{
					stolen_timer = TRUE;
					stolenwhen = 2;
				}
			}
		}
		DBGSIGSAFEFPF((stderr, "check_for_timer_pops: stolenwhen=%d\n", stolenwhen));
		/* Check for an established timer */
		deferred_timers_check_needed = TRUE;	/* Invoke timer_handler because the ext call could swallow a signal */
	}
	if (timeroot && (1 > timer_stack_count))
		DEFERRED_EXIT_HANDLING_CHECK;	/* Check for deferred timers */
	/* Now that timer handling is done, issue errors as needed */
	if (stolenwhen)
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_TIMERHANDLER, 3, current_sa.sa_handler,
			LEN_AND_STR(whenstolen[stolenwhen - 1]), save_errno);
}

/* Externally exposed routine that does a find_timer and is SIGALRM interrupt safe. */
GT_TIMER *find_timer_intr_safe(TID tid, GT_TIMER **tprev)
{
	sigset_t 	savemask;
	GT_TIMER	*tcur;
	int		rc;

	/* Before scanning timer queues, block SIGALRM signal as otherwise that signal could cause an interrupt
	 * timer routine to be driven which could in turn modify the timer queues while this mainline code is
	 * examining the very same queue. This could cause all sorts of invalid returns (of tcur and tprev)
	 * from the find_timer call below.
	 */
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);
	tcur = find_timer(tid, tprev);
	if (1 > timer_stack_count)
		SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);
	return tcur;
}