1 /*-------------------------------------------------------------------------
4 * Implement shared memory using win32 facilities
6 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
9 * $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.3 2007/11/08 14:47:41 petere Exp $
11 *-------------------------------------------------------------------------
15 #include "miscadmin.h"
16 #include "storage/ipc.h"
17 #include "storage/pg_shmem.h"
19 unsigned long UsedShmemSegID = 0;
20 void *UsedShmemSegAddr = NULL;
22 static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
25 * Generate shared memory segment name. Expand the data directory, to generate
26 * an identifier unique for this data directory. Then replace all backslashes
27 * with forward slashes, since backslashes aren't permitted in global object names.
29 * Store the shared memory segment in the Global\ namespace (requires NT2 TSE or
30 * 2000, but that's all we support for other reasons as well), to make sure you can't
31 * open two postmasters in different sessions against the same data directory.
33 * XXX: What happens with junctions? It's only someone breaking things on purpose,
34 * and this is still better than before, but we might want to do something about
35 * that sometime in the future.
38 GetSharedMemName(void)
45 bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
47 elog(FATAL, "could not get size for full pathname of datadir %s: %lu",
48 DataDir, GetLastError());
50 retptr = malloc(bufsize + 1 + 18); /* 1 NULL and 18 for
51 * Global\PostgreSQL: */
53 elog(FATAL, "could not allocate memory for shared memory name");
55 strcpy(retptr, "Global\\PostgreSQL:");
56 r = GetFullPathName(DataDir, bufsize, retptr + 11, NULL);
57 if (r == 0 || r > bufsize)
58 elog(FATAL, "could not generate full pathname for datadir %s: %lu",
59 DataDir, GetLastError());
61 for (cp = retptr; *cp; cp++)
70 * PGSharedMemoryIsInUse
72 * Is a previously-existing shmem segment still existing and in use?
74 * The point of this exercise is to detect the case where a prior postmaster
75 * crashed, but it left child backends that are still running. Therefore
76 * we only care about shmem segments that are associated with the intended
77 * DataDir. This is an important consideration since accidental matches of
78 * shmem segment IDs are reasonably common.
82 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
87 szShareMem = GetSharedMemName();
89 hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
102 * PGSharedMemoryCreate
104 * Create a shared memory segment of the given size and initialize its
107 * makePrivate means to always create a new segment, rather than attach to
108 * or recycle any existing segment. On win32, we always create a new segment,
109 * since there is no need for recycling (segments go away automatically
110 * when the last backend exits)
114 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
122 /* Room for a header? */
123 Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
125 szShareMem = GetSharedMemName();
127 UsedShmemSegAddr = NULL;
129 hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF, /* Use the pagefile */
130 NULL, /* Default security attrs */
131 PAGE_READWRITE, /* Memory is Read/Write */
132 0L, /* Size Upper 32 Bits */
133 (DWORD) size, /* Size Lower 32 bits */
138 (errmsg("could not create shared memory segment: %lu", GetLastError()),
139 errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
140 (unsigned long) size, szShareMem)));
143 * If the segment already existed, CreateFileMapping() will return a
144 * handle to the existing one.
146 if (GetLastError() == ERROR_ALREADY_EXISTS)
149 * When recycling a shared memory segment, it may take a short while
150 * before it gets dropped from the global namespace. So re-try after
151 * sleeping for a second.
153 CloseHandle(hmap); /* Close the old handle, since we got a valid
154 * one to the previous segment. */
158 hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0L, (DWORD) size, szShareMem);
161 (errmsg("could not create shared memory segment: %lu", GetLastError()),
162 errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
163 (unsigned long) size, szShareMem)));
165 if (GetLastError() == ERROR_ALREADY_EXISTS)
167 (errmsg("pre-existing shared memory block is still in use"),
168 errhint("Check if there are any old server processes still running, and terminate them.")));
174 * Make the handle inheritable
176 if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
178 (errmsg("could not create shared memory segment: %lu", GetLastError()),
179 errdetail("Failed system call was DuplicateHandle.")));
182 * Close the old, non-inheritable handle. If this fails we don't really
185 if (!CloseHandle(hmap))
186 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
189 /* Register on-exit routine to delete the new segment */
190 on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2));
193 * Get a pointer to the new shared memory segment. Map the whole segment
194 * at once, and let the system decide on the initial address.
196 memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
199 (errmsg("could not create shared memory segment: %lu", GetLastError()),
200 errdetail("Failed system call was MapViewOfFileEx.")));
205 * OK, we created a new segment. Mark it as created by this process. The
206 * order of assignments here is critical so that another Postgres process
207 * can't see the header as valid but belonging to an invalid PID!
209 hdr = (PGShmemHeader *) memAddress;
210 hdr->creatorPID = getpid();
211 hdr->magic = PGShmemMagic;
214 * Initialize space allocation status for segment.
216 hdr->totalsize = size;
217 hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
219 /* Save info for possible future use */
220 UsedShmemSegAddr = memAddress;
221 UsedShmemSegID = (unsigned long) hmap2;
227 * PGSharedMemoryReAttach
229 * Re-attach to an already existing shared memory segment. Use the
230 * handle inherited from the postmaster.
232 * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
233 * routine. The caller must have already restored them to the postmaster's
237 PGSharedMemoryReAttach(void)
240 void *origUsedShmemSegAddr = UsedShmemSegAddr;
242 Assert(UsedShmemSegAddr != NULL);
243 Assert(IsUnderPostmaster);
245 hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
247 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
248 (int) UsedShmemSegID, UsedShmemSegAddr, GetLastError());
249 if (hdr != origUsedShmemSegAddr)
250 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
251 hdr, origUsedShmemSegAddr);
252 if (hdr->magic != PGShmemMagic)
253 elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");
255 UsedShmemSegAddr = hdr; /* probably redundant */
259 * PGSharedMemoryDetach
261 * Detach from the shared memory segment, if still attached. This is not
262 * intended for use by the process that originally created the segment. Rather,
263 * this is for subprocesses that have inherited an attachment and want to
267 PGSharedMemoryDetach(void)
269 if (UsedShmemSegAddr != NULL)
271 if (!UnmapViewOfFile(UsedShmemSegAddr))
272 elog(LOG, "could not unmap view of shared memory: %lu", GetLastError());
274 UsedShmemSegAddr = NULL;
280 * pgwin32_SharedMemoryDelete(status, shmId) deletes a shared memory segment
281 * (called as an on_shmem_exit callback, hence funny argument list)
284 pgwin32_SharedMemoryDelete(int status, Datum shmId)
286 PGSharedMemoryDetach();
287 if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
288 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());