1 /*-------------------------------------------------------------------------
2 *
3 * win32_sema.c
4 * Microsoft Windows Win32 Semaphores Emulation
5 *
6 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/backend/port/win32_sema.c
10 *
11 *-------------------------------------------------------------------------
12 */
13
14 #include "postgres.h"
15
16 #include "miscadmin.h"
17 #include "storage/ipc.h"
18 #include "storage/pg_sema.h"
19
20 static HANDLE *mySemSet; /* IDs of sema sets acquired so far */
21 static int numSems; /* number of sema sets acquired so far */
22 static int maxSems; /* allocated size of mySemaSet array */
23
24 static void ReleaseSemaphores(int code, Datum arg);
25
26
27 /*
28 * Report amount of shared memory needed for semaphores
29 */
30 Size
PGSemaphoreShmemSize(int maxSemas)31 PGSemaphoreShmemSize(int maxSemas)
32 {
33 /* No shared memory needed on Windows */
34 return 0;
35 }
36
37 /*
38 * PGReserveSemaphores --- initialize semaphore support
39 *
40 * In the Win32 implementation, we acquire semaphores on-demand; the
41 * maxSemas parameter is just used to size the array that keeps track of
42 * acquired semas for subsequent releasing. We use anonymous semaphores
43 * so the semaphores are automatically freed when the last referencing
44 * process exits.
45 */
46 void
PGReserveSemaphores(int maxSemas)47 PGReserveSemaphores(int maxSemas)
48 {
49 mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
50 if (mySemSet == NULL)
51 elog(PANIC, "out of memory");
52 numSems = 0;
53 maxSems = maxSemas;
54
55 on_shmem_exit(ReleaseSemaphores, 0);
56 }
57
58 /*
59 * Release semaphores at shutdown or shmem reinitialization
60 *
61 * (called as an on_shmem_exit callback, hence funny argument list)
62 */
63 static void
ReleaseSemaphores(int code,Datum arg)64 ReleaseSemaphores(int code, Datum arg)
65 {
66 int i;
67
68 for (i = 0; i < numSems; i++)
69 CloseHandle(mySemSet[i]);
70 free(mySemSet);
71 }
72
73 /*
74 * PGSemaphoreCreate
75 *
76 * Allocate a PGSemaphore structure with initial count 1
77 */
78 PGSemaphore
PGSemaphoreCreate(void)79 PGSemaphoreCreate(void)
80 {
81 HANDLE cur_handle;
82 SECURITY_ATTRIBUTES sec_attrs;
83
84 /* Can't do this in a backend, because static state is postmaster's */
85 Assert(!IsUnderPostmaster);
86
87 if (numSems >= maxSems)
88 elog(PANIC, "too many semaphores created");
89
90 ZeroMemory(&sec_attrs, sizeof(sec_attrs));
91 sec_attrs.nLength = sizeof(sec_attrs);
92 sec_attrs.lpSecurityDescriptor = NULL;
93 sec_attrs.bInheritHandle = TRUE;
94
95 /* We don't need a named semaphore */
96 cur_handle = CreateSemaphore(&sec_attrs, 1, 32767, NULL);
97 if (cur_handle)
98 {
99 /* Successfully done */
100 mySemSet[numSems++] = cur_handle;
101 }
102 else
103 ereport(PANIC,
104 (errmsg("could not create semaphore: error code %lu",
105 GetLastError())));
106
107 return (PGSemaphore) cur_handle;
108 }
109
110 /*
111 * PGSemaphoreReset
112 *
113 * Reset a previously-initialized PGSemaphore to have count 0
114 */
115 void
PGSemaphoreReset(PGSemaphore sema)116 PGSemaphoreReset(PGSemaphore sema)
117 {
118 /*
119 * There's no direct API for this in Win32, so we have to ratchet the
120 * semaphore down to 0 with repeated trylock's.
121 */
122 while (PGSemaphoreTryLock(sema))
123 /* loop */ ;
124 }
125
126 /*
127 * PGSemaphoreLock
128 *
129 * Lock a semaphore (decrement count), blocking if count would be < 0.
130 */
131 void
PGSemaphoreLock(PGSemaphore sema)132 PGSemaphoreLock(PGSemaphore sema)
133 {
134 HANDLE wh[2];
135 bool done = false;
136
137 /*
138 * Note: pgwin32_signal_event should be first to ensure that it will be
139 * reported when multiple events are set. We want to guarantee that
140 * pending signals are serviced.
141 */
142 wh[0] = pgwin32_signal_event;
143 wh[1] = sema;
144
145 /*
146 * As in other implementations of PGSemaphoreLock, we need to check for
147 * cancel/die interrupts each time through the loop. But here, there is
148 * no hidden magic about whether the syscall will internally service a
149 * signal --- we do that ourselves.
150 */
151 while (!done)
152 {
153 DWORD rc;
154
155 CHECK_FOR_INTERRUPTS();
156
157 rc = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE);
158 switch (rc)
159 {
160 case WAIT_OBJECT_0:
161 /* Signal event is set - we have a signal to deliver */
162 pgwin32_dispatch_queued_signals();
163 break;
164 case WAIT_OBJECT_0 + 1:
165 /* We got it! */
166 done = true;
167 break;
168 case WAIT_IO_COMPLETION:
169
170 /*
171 * The system interrupted the wait to execute an I/O
172 * completion routine or asynchronous procedure call in this
173 * thread. PostgreSQL does not provoke either of these, but
174 * atypical loaded DLLs or even other processes might do so.
175 * Now, resume waiting.
176 */
177 break;
178 case WAIT_FAILED:
179 ereport(FATAL,
180 (errmsg("could not lock semaphore: error code %lu",
181 GetLastError())));
182 break;
183 default:
184 elog(FATAL, "unexpected return code from WaitForMultipleObjectsEx(): %lu", rc);
185 break;
186 }
187 }
188 }
189
190 /*
191 * PGSemaphoreUnlock
192 *
193 * Unlock a semaphore (increment count)
194 */
195 void
PGSemaphoreUnlock(PGSemaphore sema)196 PGSemaphoreUnlock(PGSemaphore sema)
197 {
198 if (!ReleaseSemaphore(sema, 1, NULL))
199 ereport(FATAL,
200 (errmsg("could not unlock semaphore: error code %lu",
201 GetLastError())));
202 }
203
204 /*
205 * PGSemaphoreTryLock
206 *
207 * Lock a semaphore only if able to do so without blocking
208 */
209 bool
PGSemaphoreTryLock(PGSemaphore sema)210 PGSemaphoreTryLock(PGSemaphore sema)
211 {
212 DWORD ret;
213
214 ret = WaitForSingleObject(sema, 0);
215
216 if (ret == WAIT_OBJECT_0)
217 {
218 /* We got it! */
219 return true;
220 }
221 else if (ret == WAIT_TIMEOUT)
222 {
223 /* Can't get it */
224 errno = EAGAIN;
225 return false;
226 }
227
228 /* Otherwise we are in trouble */
229 ereport(FATAL,
230 (errmsg("could not try-lock semaphore: error code %lu",
231 GetLastError())));
232
233 /* keep compiler quiet */
234 return false;
235 }
236