]> granicus.if.org Git - postgresql/blob - src/backend/port/win32_shmem.c
Remove IRIX port.
[postgresql] / src / backend / port / win32_shmem.c
1 /*-------------------------------------------------------------------------
2  *
3  * win32_shmem.c
4  *        Implement shared memory using win32 facilities
5  *
6  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        src/backend/port/win32_shmem.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "miscadmin.h"
16 #include "storage/ipc.h"
17 #include "storage/pg_shmem.h"
18
19 HANDLE          UsedShmemSegID = 0;
20 void       *UsedShmemSegAddr = NULL;
21 static Size UsedShmemSegSize = 0;
22
23 static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
24
25 /*
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.
29  *
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.
33  *
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.
37  */
38 static char *
39 GetSharedMemName(void)
40 {
41         char       *retptr;
42         DWORD           bufsize;
43         DWORD           r;
44         char       *cp;
45
46         bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
47         if (bufsize == 0)
48                 elog(FATAL, "could not get size for full pathname of datadir %s: error code %lu",
49                          DataDir, GetLastError());
50
51         retptr = malloc(bufsize + 18);          /* 18 for Global\PostgreSQL: */
52         if (retptr == NULL)
53                 elog(FATAL, "could not allocate memory for shared memory name");
54
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());
60
61         /*
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.
66          */
67         for (cp = retptr; *cp; cp++)
68                 if (*cp == '\\')
69                         *cp = '/';
70
71         return retptr;
72 }
73
74
75 /*
76  * PGSharedMemoryIsInUse
77  *
78  * Is a previously-existing shmem segment still existing and in use?
79  *
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.
85  *
86  */
87 bool
88 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
89 {
90         char       *szShareMem;
91         HANDLE          hmap;
92
93         szShareMem = GetSharedMemName();
94
95         hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
96
97         free(szShareMem);
98
99         if (hmap == NULL)
100                 return false;
101
102         CloseHandle(hmap);
103         return true;
104 }
105
106
107 /*
108  * PGSharedMemoryCreate
109  *
110  * Create a shared memory segment of the given size and initialize its
111  * standard header.
112  *
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)
117  *
118  */
119 PGShmemHeader *
120 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
121 {
122         void       *memAddress;
123         PGShmemHeader *hdr;
124         HANDLE          hmap,
125                                 hmap2;
126         char       *szShareMem;
127         int                     i;
128         DWORD           size_high;
129         DWORD           size_low;
130
131         /* Room for a header? */
132         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
133
134         szShareMem = GetSharedMemName();
135
136         UsedShmemSegAddr = NULL;
137
138 #ifdef _WIN64
139         size_high = size >> 32;
140 #else
141         size_high = 0;
142 #endif
143         size_low = (DWORD) size;
144
145         /*
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)
150          */
151         for (i = 0; i < 10; i++)
152         {
153                 /*
154                  * In case CreateFileMapping() doesn't set the error code to 0 on
155                  * success
156                  */
157                 SetLastError(0);
158
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 */
164                                                                  szShareMem);
165
166                 if (!hmap)
167                         ereport(FATAL,
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)));
171
172                 /*
173                  * If the segment already existed, CreateFileMapping() will return a
174                  * handle to the existing one and set ERROR_ALREADY_EXISTS.
175                  */
176                 if (GetLastError() == ERROR_ALREADY_EXISTS)
177                 {
178                         CloseHandle(hmap);      /* Close the handle, since we got a valid one
179                                                                  * to the previous segment. */
180                         hmap = NULL;
181                         Sleep(1000);
182                         continue;
183                 }
184                 break;
185         }
186
187         /*
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.
190          */
191         if (!hmap)
192                 ereport(FATAL,
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.")));
195
196         free(szShareMem);
197
198         /*
199          * Make the handle inheritable
200          */
201         if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
202                 ereport(FATAL,
203                                 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
204                                  errdetail("Failed system call was DuplicateHandle.")));
205
206         /*
207          * Close the old, non-inheritable handle. If this fails we don't really
208          * care.
209          */
210         if (!CloseHandle(hmap))
211                 elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
212
213
214         /* Register on-exit routine to delete the new segment */
215         on_shmem_exit(pgwin32_SharedMemoryDelete, PointerGetDatum(hmap2));
216
217         /*
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.
220          */
221         memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
222         if (!memAddress)
223                 ereport(FATAL,
224                                 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
225                                  errdetail("Failed system call was MapViewOfFileEx.")));
226
227
228
229         /*
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!
233          */
234         hdr = (PGShmemHeader *) memAddress;
235         hdr->creatorPID = getpid();
236         hdr->magic = PGShmemMagic;
237
238         /*
239          * Initialize space allocation status for segment.
240          */
241         hdr->totalsize = size;
242         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
243
244         /* Save info for possible future use */
245         UsedShmemSegAddr = memAddress;
246         UsedShmemSegSize = size;
247         UsedShmemSegID = hmap2;
248
249         return hdr;
250 }
251
252 /*
253  * PGSharedMemoryReAttach
254  *
255  * Re-attach to an already existing shared memory segment. Use the
256  * handle inherited from the postmaster.
257  *
258  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
259  * routine.  The caller must have already restored them to the postmaster's
260  * values.
261  */
262 void
263 PGSharedMemoryReAttach(void)
264 {
265         PGShmemHeader *hdr;
266         void       *origUsedShmemSegAddr = UsedShmemSegAddr;
267
268         Assert(UsedShmemSegAddr != NULL);
269         Assert(IsUnderPostmaster);
270
271         /*
272          * Release memory region reservation that was made by the postmaster
273          */
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());
277
278         hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
279         if (!hdr)
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");
287
288         UsedShmemSegAddr = hdr;         /* probably redundant */
289 }
290
291 /*
292  * PGSharedMemoryDetach
293  *
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
297  * get rid of it.
298  */
299 void
300 PGSharedMemoryDetach(void)
301 {
302         if (UsedShmemSegAddr != NULL)
303         {
304                 if (!UnmapViewOfFile(UsedShmemSegAddr))
305                         elog(LOG, "could not unmap view of shared memory: error code %lu", GetLastError());
306
307                 UsedShmemSegAddr = NULL;
308         }
309 }
310
311
312 /*
313  *      pgwin32_SharedMemoryDelete(status, shmId)               deletes a shared memory segment
314  *      (called as an on_shmem_exit callback, hence funny argument list)
315  */
316 static void
317 pgwin32_SharedMemoryDelete(int status, Datum shmId)
318 {
319         PGSharedMemoryDetach();
320         if (!CloseHandle(DatumGetPointer(shmId)))
321                 elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
322 }
323
324 /*
325  * pgwin32_ReserveSharedMemoryRegion(hChild)
326  *
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.
330  *
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.
337  *
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.
340  */
341 int
342 pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
343 {
344         void       *address;
345
346         Assert(UsedShmemSegAddr != NULL);
347         Assert(UsedShmemSegSize != 0);
348
349         address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
350                                                          MEM_RESERVE, PAGE_READWRITE);
351         if (address == NULL)
352         {
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());
356                 return false;
357         }
358         if (address != UsedShmemSegAddr)
359         {
360                 /*
361                  * Should never happen - in theory if allocation granularity causes
362                  * strange effects it could, so check just in case.
363                  *
364                  * Don't use FATAL since we're running in the postmaster.
365                  */
366                 elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
367                          address, UsedShmemSegAddr);
368                 VirtualFreeEx(hChild, address, 0, MEM_RELEASE);
369                 return false;
370         }
371
372         return true;
373 }