| /*------------------------------------------------------------------------- |
| * |
| * win32_sema.c |
| * Microsoft Windows Win32 Semaphores Emulation |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * |
| * IDENTIFICATION |
| * src/backend/port/win32_sema.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "miscadmin.h" |
| #include "storage/ipc.h" |
| #include "storage/pg_sema.h" |
| |
| static HANDLE *mySemSet; /* IDs of sema sets acquired so far */ |
| static int numSems; /* number of sema sets acquired so far */ |
| static int maxSems; /* allocated size of mySemaSet array */ |
| |
| static void ReleaseSemaphores(int code, Datum arg); |
| |
| |
| /* |
| * Report amount of shared memory needed for semaphores |
| */ |
| Size |
| PGSemaphoreShmemSize(int maxSemas) |
| { |
| /* No shared memory needed on Windows */ |
| return 0; |
| } |
| |
| /* |
| * PGReserveSemaphores --- initialize semaphore support |
| * |
| * In the Win32 implementation, we acquire semaphores on-demand; the |
| * maxSemas parameter is just used to size the array that keeps track of |
| * acquired semas for subsequent releasing. We use anonymous semaphores |
| * so the semaphores are automatically freed when the last referencing |
| * process exits. |
| */ |
| void |
| PGReserveSemaphores(int maxSemas) |
| { |
| mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE)); |
| if (mySemSet == NULL) |
| elog(PANIC, "out of memory"); |
| numSems = 0; |
| maxSems = maxSemas; |
| |
| on_shmem_exit(ReleaseSemaphores, 0); |
| } |
| |
| /* |
| * Release semaphores at shutdown or shmem reinitialization |
| * |
| * (called as an on_shmem_exit callback, hence funny argument list) |
| */ |
| static void |
| ReleaseSemaphores(int code, Datum arg) |
| { |
| int i; |
| |
| for (i = 0; i < numSems; i++) |
| CloseHandle(mySemSet[i]); |
| free(mySemSet); |
| } |
| |
| /* |
| * PGSemaphoreCreate |
| * |
| * Allocate a PGSemaphore structure with initial count 1 |
| */ |
| PGSemaphore |
| PGSemaphoreCreate(void) |
| { |
| HANDLE cur_handle; |
| SECURITY_ATTRIBUTES sec_attrs; |
| |
| /* Can't do this in a backend, because static state is postmaster's */ |
| Assert(!IsUnderPostmaster); |
| |
| if (numSems >= maxSems) |
| elog(PANIC, "too many semaphores created"); |
| |
| ZeroMemory(&sec_attrs, sizeof(sec_attrs)); |
| sec_attrs.nLength = sizeof(sec_attrs); |
| sec_attrs.lpSecurityDescriptor = NULL; |
| sec_attrs.bInheritHandle = TRUE; |
| |
| /* We don't need a named semaphore */ |
| cur_handle = CreateSemaphore(&sec_attrs, 1, 32767, NULL); |
| if (cur_handle) |
| { |
| /* Successfully done */ |
| mySemSet[numSems++] = cur_handle; |
| } |
| else |
| ereport(PANIC, |
| (errmsg("could not create semaphore: error code %lu", |
| GetLastError()))); |
| |
| return (PGSemaphore) cur_handle; |
| } |
| |
| /* |
| * PGSemaphoreReset |
| * |
| * Reset a previously-initialized PGSemaphore to have count 0 |
| */ |
| void |
| PGSemaphoreReset(PGSemaphore sema) |
| { |
| /* |
| * There's no direct API for this in Win32, so we have to ratchet the |
| * semaphore down to 0 with repeated trylock's. |
| */ |
| while (PGSemaphoreTryLock(sema)) |
| /* loop */ ; |
| } |
| |
| /* |
| * PGSemaphoreLock |
| * |
| * Lock a semaphore (decrement count), blocking if count would be < 0. |
| */ |
| void |
| PGSemaphoreLock(PGSemaphore sema) |
| { |
| HANDLE wh[2]; |
| bool done = false; |
| |
| /* |
| * Note: pgwin32_signal_event should be first to ensure that it will be |
| * reported when multiple events are set. We want to guarantee that |
| * pending signals are serviced. |
| */ |
| wh[0] = pgwin32_signal_event; |
| wh[1] = sema; |
| |
| /* |
| * As in other implementations of PGSemaphoreLock, we need to check for |
| * cancel/die interrupts each time through the loop. But here, there is |
| * no hidden magic about whether the syscall will internally service a |
| * signal --- we do that ourselves. |
| */ |
| while (!done) |
| { |
| DWORD rc; |
| |
| CHECK_FOR_INTERRUPTS(); |
| |
| rc = WaitForMultipleObjectsEx(2, wh, FALSE, INFINITE, TRUE); |
| switch (rc) |
| { |
| case WAIT_OBJECT_0: |
| /* Signal event is set - we have a signal to deliver */ |
| pgwin32_dispatch_queued_signals(); |
| break; |
| case WAIT_OBJECT_0 + 1: |
| /* We got it! */ |
| done = true; |
| break; |
| case WAIT_IO_COMPLETION: |
| |
| /* |
| * The system interrupted the wait to execute an I/O |
| * completion routine or asynchronous procedure call in this |
| * thread. PostgreSQL does not provoke either of these, but |
| * atypical loaded DLLs or even other processes might do so. |
| * Now, resume waiting. |
| */ |
| break; |
| case WAIT_FAILED: |
| ereport(FATAL, |
| (errmsg("could not lock semaphore: error code %lu", |
| GetLastError()))); |
| break; |
| default: |
| elog(FATAL, "unexpected return code from WaitForMultipleObjectsEx(): %lu", rc); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * PGSemaphoreUnlock |
| * |
| * Unlock a semaphore (increment count) |
| */ |
| void |
| PGSemaphoreUnlock(PGSemaphore sema) |
| { |
| if (!ReleaseSemaphore(sema, 1, NULL)) |
| ereport(FATAL, |
| (errmsg("could not unlock semaphore: error code %lu", |
| GetLastError()))); |
| } |
| |
| /* |
| * PGSemaphoreTryLock |
| * |
| * Lock a semaphore only if able to do so without blocking |
| */ |
| bool |
| PGSemaphoreTryLock(PGSemaphore sema) |
| { |
| DWORD ret; |
| |
| ret = WaitForSingleObject(sema, 0); |
| |
| if (ret == WAIT_OBJECT_0) |
| { |
| /* We got it! */ |
| return true; |
| } |
| else if (ret == WAIT_TIMEOUT) |
| { |
| /* Can't get it */ |
| errno = EAGAIN; |
| return false; |
| } |
| |
| /* Otherwise we are in trouble */ |
| ereport(FATAL, |
| (errmsg("could not try-lock semaphore: error code %lu", |
| GetLastError()))); |
| |
| /* keep compiler quiet */ |
| return false; |
| } |