1 /*-------------------------------------------------------------------------
4 * Implement shared memory using win32 facilities
6 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
9 * src/backend/port/win32_shmem.c
11 *-------------------------------------------------------------------------
15 #include "miscadmin.h"
16 #include "storage/ipc.h"
17 #include "storage/pg_shmem.h"
19 HANDLE UsedShmemSegID = 0;
20 void *UsedShmemSegAddr = NULL;
21 static Size UsedShmemSegSize = 0;
23 static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
26 * Generate shared memory segment name. Expand the data directory, to generate
27 * an identifier unique for this data directory. Then replace all backslashes
28 * with forward slashes, since backslashes aren't permitted in global object names.
30 * Store the shared memory segment in the Global\ namespace (requires NT2 TSE or
31 * 2000, but that's all we support for other reasons as well), to make sure you can't
32 * open two postmasters in different sessions against the same data directory.
34 * XXX: What happens with junctions? It's only someone breaking things on purpose,
35 * and this is still better than before, but we might want to do something about
36 * that sometime in the future.
39 GetSharedMemName(void)
46 bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
48 elog(FATAL, "could not get size for full pathname of datadir %s: error code %lu",
49 DataDir, GetLastError());
51 retptr = malloc(bufsize + 18); /* 18 for Global\PostgreSQL: */
53 elog(FATAL, "could not allocate memory for shared memory name");
55 strcpy(retptr, "Global\\PostgreSQL:");
56 r = GetFullPathName(DataDir, bufsize, retptr + 18, NULL);
57 if (r == 0 || r > bufsize)
58 elog(FATAL, "could not generate full pathname for datadir %s: error code %lu",
59 DataDir, GetLastError());
62 * XXX: Intentionally overwriting the Global\ part here. This was not the
63 * original approach, but putting it in the actual Global\ namespace
64 * causes permission errors in a lot of cases, so we leave it in the
65 * default namespace for now.
67 for (cp = retptr; *cp; cp++)
76 * PGSharedMemoryIsInUse
78 * Is a previously-existing shmem segment still existing and in use?
80 * The point of this exercise is to detect the case where a prior postmaster
81 * crashed, but it left child backends that are still running. Therefore
82 * we only care about shmem segments that are associated with the intended
83 * DataDir. This is an important consideration since accidental matches of
84 * shmem segment IDs are reasonably common.
88 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
93 szShareMem = GetSharedMemName();
95 hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
108 * PGSharedMemoryCreate
110 * Create a shared memory segment of the given size and initialize its
113 * makePrivate means to always create a new segment, rather than attach to
114 * or recycle any existing segment. On win32, we always create a new segment,
115 * since there is no need for recycling (segments go away automatically
116 * when the last backend exits)
120 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
131 /* Room for a header? */
132 Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
134 szShareMem = GetSharedMemName();
136 UsedShmemSegAddr = NULL;
139 size_high = size >> 32;
143 size_low = (DWORD) size;
146 * When recycling a shared memory segment, it may take a short while
147 * before it gets dropped from the global namespace. So re-try after
148 * sleeping for a second, and continue retrying 10 times. (both the 1
149 * second time and the 10 retries are completely arbitrary)
151 for (i = 0; i < 10; i++)
154 * In case CreateFileMapping() doesn't set the error code to 0 on
159 hmap = CreateFileMapping(INVALID_HANDLE_VALUE, /* Use the pagefile */
160 NULL, /* Default security attrs */
161 PAGE_READWRITE, /* Memory is Read/Write */
162 size_high, /* Size Upper 32 Bits */
163 size_low, /* Size Lower 32 bits */
168 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
169 errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
170 (unsigned long) size, szShareMem)));
173 * If the segment already existed, CreateFileMapping() will return a
174 * handle to the existing one and set ERROR_ALREADY_EXISTS.
176 if (GetLastError() == ERROR_ALREADY_EXISTS)
178 CloseHandle(hmap); /* Close the handle, since we got a valid one
179 * to the previous segment. */
188 * If the last call in the loop still returned ERROR_ALREADY_EXISTS, this
189 * shared memory segment exists and we assume it belongs to somebody else.
193 (errmsg("pre-existing shared memory block is still in use"),
194 errhint("Check if there are any old server processes still running, and terminate them.")));
199 * Make the handle inheritable
201 if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
203 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
204 errdetail("Failed system call was DuplicateHandle.")));
207 * Close the old, non-inheritable handle. If this fails we don't really
210 if (!CloseHandle(hmap))
211 elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
214 /* Register on-exit routine to delete the new segment */
215 on_shmem_exit(pgwin32_SharedMemoryDelete, PointerGetDatum(hmap2));
218 * Get a pointer to the new shared memory segment. Map the whole segment
219 * at once, and let the system decide on the initial address.
221 memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
224 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
225 errdetail("Failed system call was MapViewOfFileEx.")));
230 * OK, we created a new segment. Mark it as created by this process. The
231 * order of assignments here is critical so that another Postgres process
232 * can't see the header as valid but belonging to an invalid PID!
234 hdr = (PGShmemHeader *) memAddress;
235 hdr->creatorPID = getpid();
236 hdr->magic = PGShmemMagic;
239 * Initialize space allocation status for segment.
241 hdr->totalsize = size;
242 hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
244 /* Save info for possible future use */
245 UsedShmemSegAddr = memAddress;
246 UsedShmemSegSize = size;
247 UsedShmemSegID = hmap2;
253 * PGSharedMemoryReAttach
255 * Re-attach to an already existing shared memory segment. Use the
256 * handle inherited from the postmaster.
258 * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
259 * routine. The caller must have already restored them to the postmaster's
263 PGSharedMemoryReAttach(void)
266 void *origUsedShmemSegAddr = UsedShmemSegAddr;
268 Assert(UsedShmemSegAddr != NULL);
269 Assert(IsUnderPostmaster);
272 * Release memory region reservation that was made by the postmaster
274 if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0)
275 elog(FATAL, "failed to release reserved memory region (addr=%p): error code %lu",
276 UsedShmemSegAddr, GetLastError());
278 hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
280 elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
281 UsedShmemSegID, UsedShmemSegAddr, GetLastError());
282 if (hdr != origUsedShmemSegAddr)
283 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
284 hdr, origUsedShmemSegAddr);
285 if (hdr->magic != PGShmemMagic)
286 elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");
288 UsedShmemSegAddr = hdr; /* probably redundant */
292 * PGSharedMemoryDetach
294 * Detach from the shared memory segment, if still attached. This is not
295 * intended for use by the process that originally created the segment. Rather,
296 * this is for subprocesses that have inherited an attachment and want to
300 PGSharedMemoryDetach(void)
302 if (UsedShmemSegAddr != NULL)
304 if (!UnmapViewOfFile(UsedShmemSegAddr))
305 elog(LOG, "could not unmap view of shared memory: error code %lu", GetLastError());
307 UsedShmemSegAddr = NULL;
313 * pgwin32_SharedMemoryDelete(status, shmId) deletes a shared memory segment
314 * (called as an on_shmem_exit callback, hence funny argument list)
317 pgwin32_SharedMemoryDelete(int status, Datum shmId)
319 PGSharedMemoryDetach();
320 if (!CloseHandle(DatumGetPointer(shmId)))
321 elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
325 * pgwin32_ReserveSharedMemoryRegion(hChild)
327 * Reserve the memory region that will be used for shared memory in a child
328 * process. It is called before the child process starts, to make sure the
329 * memory is available.
331 * Once the child starts, DLLs loading in different order or threads getting
332 * scheduled differently may allocate memory which can conflict with the
333 * address space we need for our shared memory. By reserving the shared
334 * memory region before the child starts, and freeing it only just before we
335 * attempt to get access to the shared memory forces these allocations to
336 * be given different address ranges that don't conflict.
338 * NOTE! This function executes in the postmaster, and should for this
339 * reason not use elog(FATAL) since that would take down the postmaster.
342 pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
346 Assert(UsedShmemSegAddr != NULL);
347 Assert(UsedShmemSegSize != 0);
349 address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
350 MEM_RESERVE, PAGE_READWRITE);
353 /* Don't use FATAL since we're running in the postmaster */
354 elog(LOG, "could not reserve shared memory region (addr=%p) for child %p: error code %lu",
355 UsedShmemSegAddr, hChild, GetLastError());
358 if (address != UsedShmemSegAddr)
361 * Should never happen - in theory if allocation granularity causes
362 * strange effects it could, so check just in case.
364 * Don't use FATAL since we're running in the postmaster.
366 elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
367 address, UsedShmemSegAddr);
368 VirtualFreeEx(hChild, address, 0, MEM_RELEASE);