00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include <sys/time.h>
00018
00019 #include "storage/proc.h"
00020 #include "utils/timeout.h"
00021 #include "utils/timestamp.h"
00022
00023
00024
00025 typedef struct timeout_params
00026 {
00027 TimeoutId index;
00028
00029
00030 volatile bool indicator;
00031
00032
00033 timeout_handler_proc timeout_handler;
00034
00035 TimestampTz start_time;
00036 TimestampTz fin_time;
00037 } timeout_params;
00038
00039
00040
00041
00042 static timeout_params all_timeouts[MAX_TIMEOUTS];
00043 static bool all_timeouts_initialized = false;
00044
00045
00046
00047
00048
00049 static volatile int num_active_timeouts = 0;
00050 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 static volatile sig_atomic_t alarm_enabled = false;
00062
00063 #define disable_alarm() (alarm_enabled = false)
00064 #define enable_alarm() (alarm_enabled = true)
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080 static int
00081 find_active_timeout(TimeoutId id)
00082 {
00083 int i;
00084
00085 for (i = 0; i < num_active_timeouts; i++)
00086 {
00087 if (active_timeouts[i]->index == id)
00088 return i;
00089 }
00090
00091 return -1;
00092 }
00093
00094
00095
00096
00097
00098 static void
00099 insert_timeout(TimeoutId id, int index)
00100 {
00101 int i;
00102
00103 if (index < 0 || index > num_active_timeouts)
00104 elog(FATAL, "timeout index %d out of range 0..%d", index,
00105 num_active_timeouts);
00106
00107 for (i = num_active_timeouts - 1; i >= index; i--)
00108 active_timeouts[i + 1] = active_timeouts[i];
00109
00110 active_timeouts[index] = &all_timeouts[id];
00111
00112 num_active_timeouts++;
00113 }
00114
00115
00116
00117
00118 static void
00119 remove_timeout_index(int index)
00120 {
00121 int i;
00122
00123 if (index < 0 || index >= num_active_timeouts)
00124 elog(FATAL, "timeout index %d out of range 0..%d", index,
00125 num_active_timeouts - 1);
00126
00127 for (i = index + 1; i < num_active_timeouts; i++)
00128 active_timeouts[i - 1] = active_timeouts[i];
00129
00130 num_active_timeouts--;
00131 }
00132
00133
00134
00135
00136 static void
00137 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
00138 {
00139 int i;
00140
00141
00142 Assert(all_timeouts_initialized);
00143 Assert(all_timeouts[id].timeout_handler != NULL);
00144
00145
00146
00147
00148
00149 i = find_active_timeout(id);
00150 if (i >= 0)
00151 remove_timeout_index(i);
00152
00153
00154
00155
00156
00157 for (i = 0; i < num_active_timeouts; i++)
00158 {
00159 timeout_params *old_timeout = active_timeouts[i];
00160
00161 if (fin_time < old_timeout->fin_time)
00162 break;
00163 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
00164 break;
00165 }
00166
00167
00168
00169
00170 all_timeouts[id].indicator = false;
00171 all_timeouts[id].start_time = now;
00172 all_timeouts[id].fin_time = fin_time;
00173
00174 insert_timeout(id, i);
00175 }
00176
00177
00178
00179
00180
00181
00182
00183 static void
00184 schedule_alarm(TimestampTz now)
00185 {
00186 if (num_active_timeouts > 0)
00187 {
00188 struct itimerval timeval;
00189 long secs;
00190 int usecs;
00191
00192 MemSet(&timeval, 0, sizeof(struct itimerval));
00193
00194
00195 TimestampDifference(now, active_timeouts[0]->fin_time,
00196 &secs, &usecs);
00197
00198
00199
00200
00201
00202 if (secs == 0 && usecs == 0)
00203 usecs = 1;
00204
00205 timeval.it_value.tv_sec = secs;
00206 timeval.it_value.tv_usec = usecs;
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238 enable_alarm();
00239
00240
00241 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
00242 elog(FATAL, "could not enable SIGALRM timer: %m");
00243 }
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257 static void
00258 handle_sig_alarm(SIGNAL_ARGS)
00259 {
00260 int save_errno = errno;
00261
00262
00263
00264
00265
00266
00267 if (MyProc)
00268 SetLatch(&MyProc->procLatch);
00269
00270
00271
00272
00273 if (alarm_enabled)
00274 {
00275
00276
00277
00278
00279
00280 disable_alarm();
00281
00282 if (num_active_timeouts > 0)
00283 {
00284 TimestampTz now = GetCurrentTimestamp();
00285
00286
00287 while (num_active_timeouts > 0 &&
00288 now >= active_timeouts[0]->fin_time)
00289 {
00290 timeout_params *this_timeout = active_timeouts[0];
00291
00292
00293 remove_timeout_index(0);
00294
00295
00296 this_timeout->indicator = true;
00297
00298
00299 (*this_timeout->timeout_handler) ();
00300
00301
00302
00303
00304
00305
00306 now = GetCurrentTimestamp();
00307 }
00308
00309
00310 schedule_alarm(now);
00311 }
00312 }
00313
00314 errno = save_errno;
00315 }
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331 void
00332 InitializeTimeouts(void)
00333 {
00334 int i;
00335
00336
00337 disable_alarm();
00338
00339 num_active_timeouts = 0;
00340
00341 for (i = 0; i < MAX_TIMEOUTS; i++)
00342 {
00343 all_timeouts[i].index = i;
00344 all_timeouts[i].indicator = false;
00345 all_timeouts[i].timeout_handler = NULL;
00346 all_timeouts[i].start_time = 0;
00347 all_timeouts[i].fin_time = 0;
00348 }
00349
00350 all_timeouts_initialized = true;
00351
00352
00353 pqsignal(SIGALRM, handle_sig_alarm);
00354 }
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 TimeoutId
00365 RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
00366 {
00367 Assert(all_timeouts_initialized);
00368
00369
00370
00371 if (id >= USER_TIMEOUT)
00372 {
00373
00374 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
00375 if (all_timeouts[id].timeout_handler == NULL)
00376 break;
00377 if (id >= MAX_TIMEOUTS)
00378 ereport(FATAL,
00379 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
00380 errmsg("cannot add more timeout reasons")));
00381 }
00382
00383 Assert(all_timeouts[id].timeout_handler == NULL);
00384
00385 all_timeouts[id].timeout_handler = handler;
00386
00387 return id;
00388 }
00389
00390
00391
00392
00393
00394
00395 void
00396 enable_timeout_after(TimeoutId id, int delay_ms)
00397 {
00398 TimestampTz now;
00399 TimestampTz fin_time;
00400
00401
00402 disable_alarm();
00403
00404
00405 now = GetCurrentTimestamp();
00406 fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
00407 enable_timeout(id, now, fin_time);
00408
00409
00410 schedule_alarm(now);
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420 void
00421 enable_timeout_at(TimeoutId id, TimestampTz fin_time)
00422 {
00423 TimestampTz now;
00424
00425
00426 disable_alarm();
00427
00428
00429 now = GetCurrentTimestamp();
00430 enable_timeout(id, now, fin_time);
00431
00432
00433 schedule_alarm(now);
00434 }
00435
00436
00437
00438
00439
00440
00441
00442
00443 void
00444 enable_timeouts(const EnableTimeoutParams *timeouts, int count)
00445 {
00446 TimestampTz now;
00447 int i;
00448
00449
00450 disable_alarm();
00451
00452
00453 now = GetCurrentTimestamp();
00454
00455 for (i = 0; i < count; i++)
00456 {
00457 TimeoutId id = timeouts[i].id;
00458 TimestampTz fin_time;
00459
00460 switch (timeouts[i].type)
00461 {
00462 case TMPARAM_AFTER:
00463 fin_time = TimestampTzPlusMilliseconds(now,
00464 timeouts[i].delay_ms);
00465 enable_timeout(id, now, fin_time);
00466 break;
00467
00468 case TMPARAM_AT:
00469 enable_timeout(id, now, timeouts[i].fin_time);
00470 break;
00471
00472 default:
00473 elog(ERROR, "unrecognized timeout type %d",
00474 (int) timeouts[i].type);
00475 break;
00476 }
00477 }
00478
00479
00480 schedule_alarm(now);
00481 }
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 void
00493 disable_timeout(TimeoutId id, bool keep_indicator)
00494 {
00495 int i;
00496
00497
00498 Assert(all_timeouts_initialized);
00499 Assert(all_timeouts[id].timeout_handler != NULL);
00500
00501
00502 disable_alarm();
00503
00504
00505 i = find_active_timeout(id);
00506 if (i >= 0)
00507 remove_timeout_index(i);
00508
00509
00510 if (!keep_indicator)
00511 all_timeouts[id].indicator = false;
00512
00513
00514 if (num_active_timeouts > 0)
00515 schedule_alarm(GetCurrentTimestamp());
00516 }
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528 void
00529 disable_timeouts(const DisableTimeoutParams *timeouts, int count)
00530 {
00531 int i;
00532
00533 Assert(all_timeouts_initialized);
00534
00535
00536 disable_alarm();
00537
00538
00539 for (i = 0; i < count; i++)
00540 {
00541 TimeoutId id = timeouts[i].id;
00542 int idx;
00543
00544 Assert(all_timeouts[id].timeout_handler != NULL);
00545
00546 idx = find_active_timeout(id);
00547 if (idx >= 0)
00548 remove_timeout_index(idx);
00549
00550 if (!timeouts[i].keep_indicator)
00551 all_timeouts[id].indicator = false;
00552 }
00553
00554
00555 if (num_active_timeouts > 0)
00556 schedule_alarm(GetCurrentTimestamp());
00557 }
00558
00559
00560
00561
00562
00563 void
00564 disable_all_timeouts(bool keep_indicators)
00565 {
00566 disable_alarm();
00567
00568
00569
00570
00571
00572
00573 if (num_active_timeouts > 0)
00574 {
00575 struct itimerval timeval;
00576
00577 MemSet(&timeval, 0, sizeof(struct itimerval));
00578 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
00579 elog(FATAL, "could not disable SIGALRM timer: %m");
00580 }
00581
00582 num_active_timeouts = 0;
00583
00584 if (!keep_indicators)
00585 {
00586 int i;
00587
00588 for (i = 0; i < MAX_TIMEOUTS; i++)
00589 all_timeouts[i].indicator = false;
00590 }
00591 }
00592
00593
00594
00595
00596
00597
00598
00599
00600 bool
00601 get_timeout_indicator(TimeoutId id, bool reset_indicator)
00602 {
00603 if (all_timeouts[id].indicator)
00604 {
00605 if (reset_indicator)
00606 all_timeouts[id].indicator = false;
00607 return true;
00608 }
00609 return false;
00610 }
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620 TimestampTz
00621 get_timeout_start_time(TimeoutId id)
00622 {
00623 return all_timeouts[id].start_time;
00624 }