From 1a67e4869c74b7cf3c224b591788104946871fcd Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Thu, 24 Apr 2003 17:20:23 +0000
Subject: [PATCH] Add shared memory and semaphore routines for Win32.

Also update copyright to be ours, with approval from Jan.
---
 src/backend/port/win32/sem.c | 239 +++++++++++++++++++++++++++++++++++
 src/backend/port/win32/shm.c | 120 ++++++++++++++++++
 2 files changed, 359 insertions(+)
 create mode 100644 src/backend/port/win32/sem.c
 create mode 100644 src/backend/port/win32/shm.c

diff --git a/src/backend/port/win32/sem.c b/src/backend/port/win32/sem.c
new file mode 100644
index 00000000000..33a29651ef9
--- /dev/null
+++ b/src/backend/port/win32/sem.c
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------
+ *
+ * sem.c
+ *	  Microsoft Windows Win32 Semaphores Emulation
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "storage/shmem.h"
+
+#include <errno.h>
+
+typedef struct {
+	int			m_numSems;
+	off_t		m_semaphoreHandles;	// offset from beginning of header
+	off_t		m_semaphoreCounts;	// offset from beginning of header
+} win32_sem_set_hdr;
+
+/* Control of a semaphore pool. The pool is an area in which we stored all
+** the semIds of the pool. The first long is the number of semaphore
+** allocated in the pool followed by semaphore handles
+*/
+
+int
+semctl(int semId, int semNum, int flag, union semun semun)
+{
+	win32_sem_set_hdr* the_set = (win32_sem_set_hdr*)MAKE_PTR(semId);
+
+	/* semNum might be 0 */
+	/* semun.array contains the sem initial values */
+	int* sem_counts = (int*)((off_t)the_set + the_set->m_semaphoreCounts);
+
+	/* Fix the count of all sem of the pool to semun.array */
+	if (flag == SETALL)
+	{
+		int i;
+		struct sembuf sops;
+		sops.sem_flg = IPC_NOWAIT;
+
+		for (i = 0; i < the_set->m_numSems; ++i) {
+			if (semun.array[i] == sem_counts[i])
+				continue; /* Nothing to do */
+
+			if (semun.array[i] < sem_counts[i])
+				sops.sem_op = -1;
+			else
+				sops.sem_op = 1;
+
+			sops.sem_num = i;
+				
+			/* Quickly lock/unlock the semaphore (if we can) */
+			if (semop(semId, &sops, 1) < 0)
+				return -1;
+		}
+		return 1;
+	}
+
+	/* Fix the count of one semaphore to semun.val */
+	else if (flag == SETVAL)
+	{
+		if (semun.val != sem_counts[semNum]) {
+			struct sembuf sops;
+			sops.sem_flg = IPC_NOWAIT;
+			sops.sem_num = semNum;
+
+			if (semun.val < sem_counts[semNum])
+				sops.sem_op = -1;
+			else
+				sops.sem_op = 1;
+				
+			/* Quickly lock/unlock the semaphore (if we can) */
+			if (semop(semId, &sops, 1) < 0)
+				return -1;
+		}
+
+		return 1;
+	}
+
+	/* Delete the pool */
+	else if (flag == IPC_RMID)
+	{
+		int i;
+		HANDLE* sem_handles = (HANDLE*)((off_t)the_set + the_set->m_semaphoreHandles);
+
+		/* Loop over all semaphore to delete them */
+		for (i = 0; i < the_set->m_numSems; ++i)
+			CloseHandle(sem_handles[i]);
+
+		return 1;
+	}
+
+	/* Get the current semaphore count */
+	else if (flag == GETNCNT)
+	{
+		return the_set->m_numSems;
+	}
+
+	/* Get the current semaphore count of the first semaphore in the pool */
+	else if (flag == GETVAL)
+	{
+		return sem_counts[semNum];
+	}
+
+	/* Other commands not yet supported */
+	else
+	{
+		errno = EINVAL;
+		return -1;
+	}
+}
+
+/* Find a pool id based on IPC key */
+int
+semget(int semKey, int semNum, int flags)
+{
+	char semname[32];
+	char cur_num[20];
+	DWORD last_error;
+	char* num_part;
+	bool ans = true;
+	SECURITY_ATTRIBUTES sec_attrs;
+	HANDLE cur_handle;
+	bool found = false;
+	Size sem_set_size = sizeof(win32_sem_set_hdr) + semNum * (sizeof(HANDLE) + sizeof(int));
+	HANDLE* sem_handles = NULL;
+	int* sem_counts = NULL;
+
+	sec_attrs.nLength = sizeof(sec_attrs);
+	sec_attrs.lpSecurityDescriptor = NULL;
+	sec_attrs.bInheritHandle = TRUE;
+
+	sprintf(semname, "PG_SEMSET.%d.", semKey);
+	num_part = semname + strlen(semname);
+
+	strcpy(num_part, _itoa(_getpid() * -1, cur_num, 10));	/* For shared memory, include the pid */
+	win32_sem_set_hdr* new_set = (win32_sem_set_hdr*)ShmemInitStruct(semname, sem_set_size, &found);
+
+	if (found) {
+		/* This should *never* happen */
+		errno = EEXIST;
+		return -1;
+	}
+
+	new_set->m_numSems = semNum;
+	new_set->m_semaphoreHandles = sizeof(win32_sem_set_hdr);	// array starts after header
+	new_set->m_semaphoreCounts = new_set->m_semaphoreHandles + (sizeof(HANDLE) * semNum);
+
+	sem_handles = (HANDLE*)((off_t)new_set + new_set->m_semaphoreHandles);
+	sem_counts = (int*)((off_t)new_set + new_set->m_semaphoreCounts);
+
+	for (int i=0; i<semNum && ans; ++i) {
+		strcpy(num_part, _itoa(i, cur_num, 10));
+
+		if (flags & IPC_CREAT)
+			cur_handle = CreateSemaphore(&sec_attrs, 0, 1, semname);
+		else
+			cur_handle = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, semname);
+
+		sem_handles[i] = cur_handle;
+
+		last_error = GetLastError();
+		if (!cur_handle)
+		{
+			errno = EACCES;
+			ans = false;
+		}
+		else if (last_error == ERROR_ALREADY_EXISTS && (flags & (IPC_CREAT | IPC_EXCL)))
+		{
+			errno = EEXIST;
+			ans = false;
+		}
+	}
+
+	if (ans) {
+		return MAKE_OFFSET(new_set);
+	} else {
+		// Blow away what we've got right now...
+		for (int i=0; i<semNum; ++i) {
+			if (sem_handles[i])
+				CloseHandle(sem_handles[i]);
+			else
+				break;
+		}
+
+		return -1;
+	}
+}
+
+/* Acquire or release in the semaphore pool */
+int
+semop(int semId, struct sembuf * sops, int nsops)
+{
+	win32_sem_set_hdr* the_set = (win32_sem_set_hdr*)MAKE_PTR(semId);
+	HANDLE* sem_handles = (HANDLE*)((off_t)the_set + the_set->m_semaphoreHandles);
+	int* sem_counts = (int*)((off_t)the_set + the_set->m_semaphoreCounts);
+	HANDLE cur_handle;
+
+	if (nsops != 1) {
+		/* Not supported (we return on 1st success, and don't cancel earlier ops) */
+		errno = E2BIG;
+		return -1;
+	}
+
+	cur_handle = sem_handles[sops[0].sem_num];
+
+	if (sops[0].sem_op == -1)
+	{
+		DWORD ret;
+		if (sops[0].sem_flg & IPC_NOWAIT)
+			ret = WaitForSingleObject(cur_handle, 0);
+		else
+			ret = WaitForSingleObject(cur_handle, INFINITE);
+
+		if (ret == WAIT_OBJECT_0) {
+			/* We got it! */
+			sem_counts[sops[0].sem_num]--;
+			return 0;
+		} else if (ret == WAIT_TIMEOUT)
+			/* Couldn't get it */
+			errno = EAGAIN;
+		else
+			errno = EIDRM;
+	}
+	else if (sops[0].sem_op > 0) {
+		/* Don't want the lock anymore */
+		sem_counts[sops[0].sem_num]++;
+		ReleaseSemaphore(cur_handle, sops[0].sem_op, NULL);
+		return 0;
+	}
+	else
+		/* Not supported */
+		errno = ERANGE;
+
+	/* If we get down here, then something is wrong */
+	return -1;
+}
diff --git a/src/backend/port/win32/shm.c b/src/backend/port/win32/shm.c
new file mode 100644
index 00000000000..20aaf1f6a4d
--- /dev/null
+++ b/src/backend/port/win32/shm.c
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * shm.c
+ *	  Microsoft Windows Win32 Shared Memory Emulation
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "windows.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+static DWORD s_segsize = 0;
+
+/* Detach from a shared mem area based on its address */
+int
+shmdt(const void *shmaddr)
+{
+	if (UnmapViewOfFile(shmaddr))
+		return 0;
+	else
+		return -1;
+}
+
+/* Attach to an existing area */
+void *
+shmat(int memId, void* shmaddr, int flag)
+{
+	/* KEW_TODO -- shmat needs to count # attached to shared mem */
+	void *lpmem = MapViewOfFileEx((HANDLE)memId,
+			  FILE_MAP_WRITE | FILE_MAP_READ,
+			  0, 0, /* (DWORD)pshmdsc->segsize */ s_segsize, shmaddr);
+
+	if (lpmem == NULL) {
+		lpmem = (void *)-1;
+		errno = GetLastError();
+	}
+
+    return lpmem;
+}
+
+/* Control a shared mem area */
+int
+shmctl(int shmid, int flag, struct shmid_ds * dummy)
+{
+	if (flag == IPC_RMID)
+	{
+		/* Delete the area */
+		CloseHandle((HANDLE)shmid);
+		return 0;
+	}
+	if (flag == IPC_STAT)
+	{
+		/* Can only test for if exists */
+		int hmap = shmget(shmid, 0, 0);
+		if (hmap < 0) {
+			/* Shared memory does not exist */
+			errno = EINVAL;
+			return -1;
+		}
+		else {
+			/* Shared memory does exist and must be in use */
+			shmctl(hmap, IPC_RMID, NULL);	/* Release our hold on it */
+			errno = 0;
+			return 0;
+		}
+	}
+
+	errno = EINVAL;
+	return -1;
+}
+
+/* Get an area based on the IPC key */
+int
+shmget(int memKey, int size, int flag)
+{
+    HANDLE  hmap;
+	char szShareMem[32];
+	DWORD dwRet;
+
+    s_segsize = size;
+    sprintf(szShareMem, "sharemem.%d", memKey);
+
+	if (flag & IPC_CREAT) {
+		hmap = CreateFileMapping((HANDLE)0xFFFFFFFF,  /* Use the swap file    */
+					 NULL,
+					 PAGE_READWRITE,      /* Memory is Read/Write */
+					 0L,                  /* Size Upper 32 Bits   */
+					 (DWORD)s_segsize,      /* Size Lower 32 bits*/
+					 szShareMem);
+	}
+	else {
+		hmap = OpenFileMapping(FILE_MAP_ALL_ACCESS,
+								FALSE,
+								szShareMem);
+		if (!hmap) {
+			errno = ENOENT;
+			return -1;
+		}
+	}
+
+	dwRet = GetLastError();
+	if (dwRet == ERROR_ALREADY_EXISTS && hmap && (flag & (IPC_CREAT | IPC_EXCL))) {
+		/* Caller wanted to create the segment -- error if already exists */
+		CloseHandle(hmap);
+		errno = EEXIST;
+		return -1;
+	}
+	else if (!hmap)
+	{
+		/* Unable to get shared memory */
+		return -1;
+	}
+
+    return (int)hmap;
+}
-- 
GitLab