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