]> granicus.if.org Git - postgresql/blob - src/backend/port/win32_shmem.c
Remove non-ascii characters from source code.
[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-2009, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.12 2009/07/24 20:12:42 mha Exp $
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 unsigned long 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: %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: %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
129         /* Room for a header? */
130         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
131
132         szShareMem = GetSharedMemName();
133
134         UsedShmemSegAddr = NULL;
135
136         /*
137          * When recycling a shared memory segment, it may take a short while
138          * before it gets dropped from the global namespace. So re-try after
139          * sleeping for a second, and continue retrying 10 times. (both the 1
140          * second time and the 10 retries are completely arbitrary)
141          */
142         for (i = 0; i < 10; i++)
143         {
144                 /*
145                  * In case CreateFileMapping() doesn't set the error code to 0 on
146                  * success
147                  */
148                 SetLastError(0);
149
150                 hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF,   /* Use the pagefile */
151                                                                  NULL,  /* Default security attrs */
152                                                                  PAGE_READWRITE,                /* Memory is Read/Write */
153                                                                  0L,    /* Size Upper 32 Bits   */
154                                                                  (DWORD) size,  /* Size Lower 32 bits */
155                                                                  szShareMem);
156
157                 if (!hmap)
158                         ereport(FATAL,
159                                         (errmsg("could not create shared memory segment: %lu", GetLastError()),
160                                          errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
161                                                            (unsigned long) size, szShareMem)));
162
163                 /*
164                  * If the segment already existed, CreateFileMapping() will return a
165                  * handle to the existing one and set ERROR_ALREADY_EXISTS.
166                  */
167                 if (GetLastError() == ERROR_ALREADY_EXISTS)
168                 {
169                         CloseHandle(hmap);      /* Close the handle, since we got a valid one
170                                                                  * to the previous segment. */
171                         hmap = NULL;
172                         Sleep(1000);
173                         continue;
174                 }
175                 break;
176         }
177
178         /*
179          * If the last call in the loop still returned ERROR_ALREADY_EXISTS, this
180          * shared memory segment exists and we assume it belongs to somebody else.
181          */
182         if (!hmap)
183                 ereport(FATAL,
184                                 (errmsg("pre-existing shared memory block is still in use"),
185                                  errhint("Check if there are any old server processes still running, and terminate them.")));
186
187         free(szShareMem);
188
189         /*
190          * Make the handle inheritable
191          */
192         if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
193                 ereport(FATAL,
194                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
195                                  errdetail("Failed system call was DuplicateHandle.")));
196
197         /*
198          * Close the old, non-inheritable handle. If this fails we don't really
199          * care.
200          */
201         if (!CloseHandle(hmap))
202                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
203
204
205         /* Register on-exit routine to delete the new segment */
206         on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2));
207
208         /*
209          * Get a pointer to the new shared memory segment. Map the whole segment
210          * at once, and let the system decide on the initial address.
211          */
212         memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
213         if (!memAddress)
214                 ereport(FATAL,
215                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
216                                  errdetail("Failed system call was MapViewOfFileEx.")));
217
218
219
220         /*
221          * OK, we created a new segment.  Mark it as created by this process. The
222          * order of assignments here is critical so that another Postgres process
223          * can't see the header as valid but belonging to an invalid PID!
224          */
225         hdr = (PGShmemHeader *) memAddress;
226         hdr->creatorPID = getpid();
227         hdr->magic = PGShmemMagic;
228
229         /*
230          * Initialize space allocation status for segment.
231          */
232         hdr->totalsize = size;
233         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
234
235         /* Save info for possible future use */
236         UsedShmemSegAddr = memAddress;
237         UsedShmemSegSize = size;
238         UsedShmemSegID = (unsigned long) hmap2;
239
240         return hdr;
241 }
242
243 /*
244  * PGSharedMemoryReAttach
245  *
246  * Re-attach to an already existing shared memory segment. Use the
247  * handle inherited from the postmaster.
248  *
249  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
250  * routine.  The caller must have already restored them to the postmaster's
251  * values.
252  */
253 void
254 PGSharedMemoryReAttach(void)
255 {
256         PGShmemHeader *hdr;
257         void       *origUsedShmemSegAddr = UsedShmemSegAddr;
258
259         Assert(UsedShmemSegAddr != NULL);
260         Assert(IsUnderPostmaster);
261
262         /*
263          * Release memory region reservation that was made by the postmaster
264          */
265         if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0)
266                 elog(FATAL, "failed to release reserved memory region (addr=%p): %lu",
267                          UsedShmemSegAddr, GetLastError());
268
269         hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
270         if (!hdr)
271                 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
272                          (int) UsedShmemSegID, UsedShmemSegAddr, GetLastError());
273         if (hdr != origUsedShmemSegAddr)
274                 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
275                          hdr, origUsedShmemSegAddr);
276         if (hdr->magic != PGShmemMagic)
277                 elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");
278
279         UsedShmemSegAddr = hdr;         /* probably redundant */
280 }
281
282 /*
283  * PGSharedMemoryDetach
284  *
285  * Detach from the shared memory segment, if still attached.  This is not
286  * intended for use by the process that originally created the segment. Rather,
287  * this is for subprocesses that have inherited an attachment and want to
288  * get rid of it.
289  */
290 void
291 PGSharedMemoryDetach(void)
292 {
293         if (UsedShmemSegAddr != NULL)
294         {
295                 if (!UnmapViewOfFile(UsedShmemSegAddr))
296                         elog(LOG, "could not unmap view of shared memory: %lu", GetLastError());
297
298                 UsedShmemSegAddr = NULL;
299         }
300 }
301
302
303 /*
304  *      pgwin32_SharedMemoryDelete(status, shmId)               deletes a shared memory segment
305  *      (called as an on_shmem_exit callback, hence funny argument list)
306  */
307 static void
308 pgwin32_SharedMemoryDelete(int status, Datum shmId)
309 {
310         PGSharedMemoryDetach();
311         if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
312                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
313 }
314
315 /*
316  * pgwin32_ReserveSharedMemoryRegion(hChild)
317  *
318  * Reserve the memory region that will be used for shared memory in a child
319  * process. It is called before the child process starts, to make sure the
320  * memory is available.
321  *
322  * Once the child starts, DLLs loading in different order or threads getting
323  * scheduled differently may allocate memory which can conflict with the
324  * address space we need for our shared memory. By reserving the shared
325  * memory region before the child starts, and freeing it only just before we
326  * attempt to get access to the shared memory forces these allocations to
327  * be given different address ranges that don't conflict.
328  *
329  * NOTE! This function executes in the postmaster, and should for this
330  * reason not use elog(FATAL) since that would take down the postmaster.
331  */
332 int
333 pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
334 {
335         void *address;
336
337         Assert(UsedShmemSegAddr != NULL);
338         Assert(UsedShmemSegSize != 0);
339
340         address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
341                                                                 MEM_RESERVE, PAGE_READWRITE);
342         if (address == NULL) {
343                 /* Don't use FATAL since we're running in the postmaster */
344                 elog(LOG, "could not reserve shared memory region (addr=%p) for child %lu: %lu",
345                          UsedShmemSegAddr, hChild, GetLastError());
346                 return false;
347         }
348         if (address != UsedShmemSegAddr)
349         {
350                 /*
351                  * Should never happen - in theory if allocation granularity causes strange
352                  * effects it could, so check just in case.
353                  *
354                  * Don't use FATAL since we're running in the postmaster.
355                  */
356             elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
357                          address, UsedShmemSegAddr);
358                 VirtualFreeEx(hChild, address, 0, MEM_RELEASE);
359                 return false;
360         }
361
362         return true;
363 }