From 18d82d03b51f1d34e8b076e89e54a4c8e0818343 Mon Sep 17 00:00:00 2001 From: Magnus Hagander <magnus@hagander.net> Date: Wed, 21 Mar 2007 14:39:23 +0000 Subject: [PATCH] Native shared memory implementation for win32. Uses same underlying tech as before, but not the sysv emulation layer. --- configure | 10 +- configure.in | 12 +- src/backend/port/sysv_shmem.c | 16 +- src/backend/port/win32/Makefile | 4 +- src/backend/port/win32/shmem.c | 128 -------------- src/backend/port/win32_shmem.c | 287 ++++++++++++++++++++++++++++++++ src/tools/msvc/Mkvcbuild.pm | 4 +- 7 files changed, 310 insertions(+), 151 deletions(-) delete mode 100644 src/backend/port/win32/shmem.c create mode 100644 src/backend/port/win32_shmem.c diff --git a/configure b/configure index b53acb350b2..71dbaee0aa4 100755 --- a/configure +++ b/configure @@ -22278,13 +22278,21 @@ fi # Select shared-memory implementation type. +if test "$PORTNAME" != "win32"; then cat >>confdefs.h <<\_ACEOF #define USE_SYSV_SHARED_MEMORY 1 _ACEOF -SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" + SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" +else + +cat >>confdefs.h <<\_ACEOF +#define USE_WIN32_SHARED_MEMORY 1 +_ACEOF + SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c" +fi # If not set in template file, set bytes to use libc memset() if test x"$MEMSET_LOOP_LIMIT" = x"" ; then diff --git a/configure.in b/configure.in index e5d72eb1d30..273f79bf5f5 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -dnl $PostgreSQL: pgsql/configure.in,v 1.502 2007/02/21 15:12:39 momjian Exp $ +dnl $PostgreSQL: pgsql/configure.in,v 1.503 2007/03/21 14:39:23 mha Exp $ dnl dnl Developers, please strive to achieve this order: dnl @@ -1367,9 +1367,13 @@ fi # Select shared-memory implementation type. -AC_DEFINE(USE_SYSV_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.]) -SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" - +if test "$PORTNAME" != "win32"; then + AC_DEFINE(USE_SYSV_SHARED_MEMORY, 1, [Define to select SysV-style shared memory.]) + SHMEM_IMPLEMENTATION="src/backend/port/sysv_shmem.c" +else + AC_DEFINE(USE_WIN32_SHARED_MEMORY, 1, [Define to select Win32-style shared memory.]) + SHMEM_IMPLEMENTATION="src/backend/port/win32_shmem.c" +fi # If not set in template file, set bytes to use libc memset() if test x"$MEMSET_LOOP_LIMIT" = x"" ; then diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 5488bc0510c..8677752e30d 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -10,7 +10,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.49 2007/02/06 16:20:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.50 2007/03/21 14:39:23 mha Exp $ * *------------------------------------------------------------------------- */ @@ -195,11 +195,8 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) { IpcMemoryId shmId = (IpcMemoryId) id2; struct shmid_ds shmStat; - -#ifndef WIN32 struct stat statbuf; PGShmemHeader *hdr; -#endif /* * We detect whether a shared memory segment is in use by seeing whether @@ -238,11 +235,8 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) /* * Try to attach to the segment and see if it matches our data directory. * This avoids shmid-conflict problems on machines that are running - * several postmasters under the same userid. On Windows, which doesn't - * have useful inode numbers, we can't do this so we punt and assume there - * is a conflict. + * several postmasters under the same userid. */ -#ifndef WIN32 if (stat(DataDir, &statbuf) < 0) return true; /* if can't stat, be conservative */ @@ -265,7 +259,6 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) /* Trouble --- looks a lot like there's still live backends */ shmdt((void *) hdr); -#endif return true; } @@ -296,10 +289,7 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port) void *memAddress; PGShmemHeader *hdr; IpcMemoryId shmid; - -#ifndef WIN32 struct stat statbuf; -#endif /* Room for a header? */ Assert(size > MAXALIGN(sizeof(PGShmemHeader))); @@ -372,7 +362,6 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port) hdr->creatorPID = getpid(); hdr->magic = PGShmemMagic; -#ifndef WIN32 /* Fill in the data directory ID info, too */ if (stat(DataDir, &statbuf) < 0) ereport(FATAL, @@ -381,7 +370,6 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port) DataDir))); hdr->device = statbuf.st_dev; hdr->inode = statbuf.st_ino; -#endif /* * Initialize space allocation status for segment. diff --git a/src/backend/port/win32/Makefile b/src/backend/port/win32/Makefile index daa98f65519..620d921eddb 100644 --- a/src/backend/port/win32/Makefile +++ b/src/backend/port/win32/Makefile @@ -4,7 +4,7 @@ # Makefile for backend/port/win32 # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/port/win32/Makefile,v 1.9 2007/01/20 17:16:12 petere Exp $ +# $PostgreSQL: pgsql/src/backend/port/win32/Makefile,v 1.10 2007/03/21 14:39:23 mha Exp $ # #------------------------------------------------------------------------- @@ -12,7 +12,7 @@ subdir = src/backend/port/win32 top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = shmem.o timer.o socket.o signal.o security.o +OBJS = timer.o socket.o signal.o security.o all: SUBSYS.o diff --git a/src/backend/port/win32/shmem.c b/src/backend/port/win32/shmem.c deleted file mode 100644 index 18dac2f553d..00000000000 --- a/src/backend/port/win32/shmem.c +++ /dev/null @@ -1,128 +0,0 @@ -/*------------------------------------------------------------------------- - * - * shmem.c - * Microsoft Windows Win32 Shared Memory Emulation - * - * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group - * - * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/win32/shmem.c,v 1.14 2007/01/05 22:19:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -static DWORD s_segsize = 0; - -/* Detach from a shared mem area based on its address */ -int -shmdt(const void *shmaddr) -{ - if (UnmapViewOfFile((LPCVOID *) shmaddr)) - return 0; - else - return -1; -} - -/* Attach to an existing area */ -void * -shmat(int memId, void *shmaddr, int flag) -{ - /* 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 */ 0 /* s_segsize */ , shmaddr); - - if (lpmem == NULL) - { - lpmem = (void *) -1; - _dosmaperr(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, "PostgreSQL.%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 */ - _dosmaperr(GetLastError()); - return -1; - } - - return (int) hmap; -} diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c new file mode 100644 index 00000000000..b912be54ecf --- /dev/null +++ b/src/backend/port/win32_shmem.c @@ -0,0 +1,287 @@ +/*------------------------------------------------------------------------- + * + * win32_shmem.c + * Implement shared memory using win32 facilities + * + * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.1 2007/03/21 14:39:23 mha Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "miscadmin.h" +#include "storage/ipc.h" +#include "storage/pg_shmem.h" + +unsigned long UsedShmemSegID = 0; +void *UsedShmemSegAddr = NULL; + +static void pgwin32_SharedMemoryDelete(int status, Datum shmId); + +/* + * Generate shared memory segment name. Expand the data directory, to generate + * an identifier unique for this data directory. Then replace all backslashes + * with forward slashes, since backslashes aren't permitted in global object names. + * + * Store the shared memory segment in the Global\ namespace (requires NT2 TSE or + * 2000, but that's all we support for other reasons as well), to make sure you can't + * open two postmasters in different sessions against the same data directory. + * + * XXX: What happens with junctions? It's only someone breaking things on purpose, + * and this is still better than before, but we might want to do something about + * that sometime in the future. + */ +static char * +GetSharedMemName(void) +{ + char *retptr; + DWORD bufsize; + DWORD r; + char *cp; + + bufsize = GetFullPathName(DataDir, 0, NULL, NULL); + if (bufsize == 0) + elog(FATAL, "could not get size for full pathname of datadir %s: %lu", + DataDir, GetLastError()); + + retptr = malloc(bufsize + 1 + 18); /* 1 NULL and 18 for + * Global\PostgreSQL: */ + if (retptr == NULL) + elog(FATAL, "could not allocate memory for shared memory name"); + + strcpy(retptr, "Global\\PostgreSQL:"); + r = GetFullPathName(DataDir, bufsize, retptr + 11, NULL); + if (r == 0 || r > bufsize) + elog(FATAL, "could not generate full pathname for datadir %s: %lu", + DataDir, GetLastError()); + + for (cp = retptr; *cp; cp++) + if (*cp == '\\') + *cp = '/'; + + return retptr; +} + + +/* + * PGSharedMemoryIsInUse + * + * Is a previously-existing shmem segment still existing and in use? + * + * The point of this exercise is to detect the case where a prior postmaster + * crashed, but it left child backends that are still running. Therefore + * we only care about shmem segments that are associated with the intended + * DataDir. This is an important consideration since accidental matches of + * shmem segment IDs are reasonably common. + * + */ +bool +PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) +{ + char *szShareMem; + HANDLE hmap; + + szShareMem = GetSharedMemName(); + + hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem); + + free(szShareMem); + + if (hmap == NULL) + return false; + + CloseHandle(hmap); + return true; +} + + +/* + * PGSharedMemoryCreate + * + * Create a shared memory segment of the given size and initialize its + * standard header. + * + * makePrivate means to always create a new segment, rather than attach to + * or recycle any existing segment. On win32, we always create a new segment, + * since there is no need for recycling (segments go away automatically + * when the last backend exits) + * + */ +PGShmemHeader * +PGSharedMemoryCreate(Size size, bool makePrivate, int port) +{ + void *memAddress; + PGShmemHeader *hdr; + HANDLE hmap, + hmap2; + char *szShareMem; + + /* Room for a header? */ + Assert(size > MAXALIGN(sizeof(PGShmemHeader))); + + szShareMem = GetSharedMemName(); + + UsedShmemSegAddr = NULL; + + hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF, /* Use the pagefile */ + NULL, /* Default security attrs */ + PAGE_READWRITE, /* Memory is Read/Write */ + 0L, /* Size Upper 32 Bits */ + (DWORD) size, /* Size Lower 32 bits */ + szShareMem); + + if (!hmap) + ereport(FATAL, + (errmsg("could not create shared memory segment: %lu", GetLastError()), + errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s)", size, szShareMem))); + + /* + * If the segment already existed, CreateFileMapping() will return a + * handle to the existing one. + */ + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + /* + * When recycling a shared memory segment, it may take a short while + * before it gets dropped from the global namespace. So re-try after + * sleeping for a second. + */ + CloseHandle(hmap); /* Close the old handle, since we got a valid + * one to the previous segment. */ + + Sleep(1000); + + hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0L, (DWORD) size, szShareMem); + if (!hmap) + ereport(FATAL, + (errmsg("could not create shared memory segment: %lu", GetLastError()), + errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s)", size, szShareMem))); + + if (GetLastError() == ERROR_ALREADY_EXISTS) + ereport(FATAL, + (errmsg("pre-existing shared memory block is still in use"), + errhint("Check if there are any old server processes still running, and terminate them."))); + } + + free(szShareMem); + + /* + * Make the handle inheritable + */ + if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS)) + ereport(FATAL, + (errmsg("could not create shared memory segment: %lu", GetLastError()), + errdetail("Failed system call was DuplicateHandle"))); + + /* + * Close the old, non-inheritable handle. If this fails we don't really + * care. + */ + if (!CloseHandle(hmap)) + elog(LOG, "could not close handle to shared memory: %lu", GetLastError()); + + + /* Register on-exit routine to delete the new segment */ + on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2)); + + /* + * Get a pointer to the new shared memory segment. Map the whole segment + * at once, and let the system decide on the initial address. + */ + memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL); + if (!memAddress) + ereport(FATAL, + (errmsg("could not create shared memory segment: %lu", GetLastError()), + errdetail("Failed system call was MapViewOfFileEx"))); + + + + /* + * OK, we created a new segment. Mark it as created by this process. The + * order of assignments here is critical so that another Postgres process + * can't see the header as valid but belonging to an invalid PID! + */ + hdr = (PGShmemHeader *) memAddress; + hdr->creatorPID = getpid(); + hdr->magic = PGShmemMagic; + + /* + * Initialize space allocation status for segment. + */ + hdr->totalsize = size; + hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader)); + + /* Save info for possible future use */ + UsedShmemSegAddr = memAddress; + UsedShmemSegID = (unsigned long) hmap2; + + return hdr; +} + +/* + * PGSharedMemoryReAttach + * + * Re-attach to an already existing shared memory segment. Use the + * handle inherited from the postmaster. + * + * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this + * routine. The caller must have already restored them to the postmaster's + * values. + */ +void +PGSharedMemoryReAttach(void) +{ + PGShmemHeader *hdr; + void *origUsedShmemSegAddr = UsedShmemSegAddr; + + Assert(UsedShmemSegAddr != NULL); + Assert(IsUnderPostmaster); + + hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr); + if (!hdr) + elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu", + (int) UsedShmemSegID, UsedShmemSegAddr, GetLastError()); + if (hdr != origUsedShmemSegAddr) + elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)", + hdr, origUsedShmemSegAddr); + if (hdr->magic != PGShmemMagic) + elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory"); + + UsedShmemSegAddr = hdr; /* probably redundant */ +} + +/* + * PGSharedMemoryDetach + * + * Detach from the shared memory segment, if still attached. This is not + * intended for use by the process that originally created the segment. Rather, + * this is for subprocesses that have inherited an attachment and want to + * get rid of it. + */ +void +PGSharedMemoryDetach(void) +{ + if (UsedShmemSegAddr != NULL) + { + if (!UnmapViewOfFile(UsedShmemSegAddr)) + elog(LOG, "could not unmap view of shared memory: %lu", GetLastError()); + + UsedShmemSegAddr = NULL; + } +} + + +/* + * pgwin32_SharedMemoryDelete(status, shmId) deletes a shared memory segment + * (called as an on_shmem_exit callback, hence funny argument list) + */ +static void +pgwin32_SharedMemoryDelete(int status, Datum shmId) +{ + PGSharedMemoryDetach(); + if (!CloseHandle((HANDLE) DatumGetInt32(shmId))) + elog(LOG, "could not close handle to shared memory: %lu", GetLastError()); +} diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 9837cc77139..87f3595ce8f 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -2,7 +2,7 @@ package Mkvcbuild; # # Package that generates build files for msvc build # -# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.3 2007/03/19 09:34:09 mha Exp $ +# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.4 2007/03/21 14:39:23 mha Exp $ # use Carp; use Win32; @@ -58,7 +58,7 @@ sub mkvcbuild $postgres->AddFile('src\backend\utils\fmgrtab.c'); $postgres->ReplaceFile('src\backend\port\dynloader.c','src\backend\port\dynloader\win32.c'); $postgres->ReplaceFile('src\backend\port\pg_sema.c','src\backend\port\win32_sema.c'); - $postgres->ReplaceFile('src\backend\port\pg_shmem.c','src\backend\port\sysv_shmem.c'); + $postgres->ReplaceFile('src\backend\port\pg_shmem.c','src\backend\port\win32_shmem.c'); $postgres->AddFiles('src\port',@pgportfiles); $postgres->AddDir('src\timezone'); $postgres->AddFiles('src\backend\parser','scan.l','gram.y'); -- GitLab