1 /*
2  * This file and its contents are licensed under the Apache License 2.0.
3  * Please see the included NOTICE for copyright information and
4  * LICENSE-APACHE for a copy of the license.
5  */
6 #include <postgres.h>
7 #include <miscadmin.h>
8 #include <postmaster/bgworker.h>
9 #include <storage/ipc.h>
10 #include <storage/latch.h>
11 #include <storage/lwlock.h>
12 #include <storage/proc.h>
13 #include <storage/shmem.h>
14 #include <utils/jsonb.h>
15 #include <utils/timestamp.h>
16 #include <utils/snapmgr.h>
17 #include <utils/memutils.h>
18 #include <access/xact.h>
19 #include <pgstat.h>
20 
21 #include "timer.h"
22 #include "compat/compat.h"
23 #include "config.h"
24 
25 #define MAX_TIMEOUT (5 * INT64CONST(1000))
26 #define MILLISECS_PER_SEC INT64CONST(1000)
27 #define USECS_PER_MILLISEC INT64CONST(1000)
28 
29 static inline void
on_postmaster_death(void)30 on_postmaster_death(void)
31 {
32 	/*
33 	 * Don't call exit hooks cause we want to bail out quickly. We don't care
34 	 * about cleaning up shared memory in this case anyway since it's
35 	 * potentially corrupt.
36 	 */
37 	on_exit_reset();
38 	ereport(FATAL,
39 			(errcode(ERRCODE_ADMIN_SHUTDOWN),
40 			 errmsg("postmaster exited while timescaledb scheduler was working")));
41 }
42 
43 static int64
get_timeout_millisec(TimestampTz by_time)44 get_timeout_millisec(TimestampTz by_time)
45 {
46 	long timeout_sec = 0;
47 	int timeout_usec = 0;
48 
49 	if (TIMESTAMP_IS_NOBEGIN(by_time))
50 		return 0;
51 
52 	if (TIMESTAMP_IS_NOEND(by_time))
53 		return PG_INT64_MAX;
54 
55 	TimestampDifference(GetCurrentTimestamp(), by_time, &timeout_sec, &timeout_usec);
56 
57 	if (timeout_sec < 0 || timeout_usec < 0)
58 		return 0;
59 
60 	return (int64)(timeout_sec * MILLISECS_PER_SEC + ((int64) timeout_usec) / USECS_PER_MILLISEC);
61 }
62 
63 static bool
wait_using_wait_latch(TimestampTz until)64 wait_using_wait_latch(TimestampTz until)
65 {
66 	int wl_rc;
67 
68 	int64 timeout = get_timeout_millisec(until);
69 
70 	Assert(timeout >= 0 && "get_timeout_millisec underflow");
71 
72 	if (timeout > MAX_TIMEOUT)
73 		timeout = MAX_TIMEOUT;
74 
75 	/* Wait latch requires timeout to be <= INT_MAX */
76 	if ((int64) timeout > (int64) INT_MAX)
77 		timeout = INT_MAX;
78 
79 	wl_rc = WaitLatch(MyLatch,
80 					  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
81 					  timeout,
82 					  PG_WAIT_EXTENSION);
83 	ResetLatch(MyLatch);
84 	if (wl_rc & WL_POSTMASTER_DEATH)
85 		on_postmaster_death();
86 
87 	return true;
88 }
89 
90 static const Timer standard_timer = {
91 	.get_current_timestamp = GetCurrentTimestamp,
92 	.wait = wait_using_wait_latch,
93 };
94 
95 static const Timer *current_timer_implementation = &standard_timer;
96 
97 static inline const Timer *
timer_get()98 timer_get()
99 {
100 	return current_timer_implementation;
101 }
102 
103 bool
ts_timer_wait(TimestampTz until)104 ts_timer_wait(TimestampTz until)
105 {
106 	return timer_get()->wait(until);
107 }
108 
109 TimestampTz
ts_timer_get_current_timestamp()110 ts_timer_get_current_timestamp()
111 {
112 	return timer_get()->get_current_timestamp();
113 }
114 
115 #ifdef TS_DEBUG
116 void
ts_timer_set(const Timer * timer)117 ts_timer_set(const Timer *timer)
118 {
119 	current_timer_implementation = timer;
120 }
121 
122 const Timer *
ts_get_standard_timer()123 ts_get_standard_timer()
124 {
125 	return &standard_timer;
126 }
127 #endif
128