]> granicus.if.org Git - postgresql/blob - src/backend/port/win32_shmem.c
Make the win32 shared memory code try 10 times instead of one if
[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.9 2009/05/05 09:48:51 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
22 static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
23
24 /*
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.
28  *
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.
32  *
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.
36  */
37 static char *
38 GetSharedMemName(void)
39 {
40         char       *retptr;
41         DWORD           bufsize;
42         DWORD           r;
43         char       *cp;
44
45         bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
46         if (bufsize == 0)
47                 elog(FATAL, "could not get size for full pathname of datadir %s: %lu",
48                          DataDir, GetLastError());
49
50         retptr = malloc(bufsize + 18);          /* 18 for Global\PostgreSQL: */
51         if (retptr == NULL)
52                 elog(FATAL, "could not allocate memory for shared memory name");
53
54         strcpy(retptr, "Global\\PostgreSQL:");
55         r = GetFullPathName(DataDir, bufsize, retptr + 18, NULL);
56         if (r == 0 || r > bufsize)
57                 elog(FATAL, "could not generate full pathname for datadir %s: %lu",
58                          DataDir, GetLastError());
59
60         /* 
61          * XXX: Intentionally overwriting the Global\ part here. This was not the
62          * original approach, but putting it in the actual Global\ namespace
63          * causes permission errors in a lot of cases, so we leave it in
64          * the default namespace for now.
65          */
66         for (cp = retptr; *cp; cp++)
67                 if (*cp == '\\')
68                         *cp = '/';
69
70         return retptr;
71 }
72
73
74 /*
75  * PGSharedMemoryIsInUse
76  *
77  * Is a previously-existing shmem segment still existing and in use?
78  *
79  * The point of this exercise is to detect the case where a prior postmaster
80  * crashed, but it left child backends that are still running.  Therefore
81  * we only care about shmem segments that are associated with the intended
82  * DataDir.  This is an important consideration since accidental matches of
83  * shmem segment IDs are reasonably common.
84  *
85  */
86 bool
87 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
88 {
89         char       *szShareMem;
90         HANDLE          hmap;
91
92         szShareMem = GetSharedMemName();
93
94         hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
95
96         free(szShareMem);
97
98         if (hmap == NULL)
99                 return false;
100
101         CloseHandle(hmap);
102         return true;
103 }
104
105
106 /*
107  * PGSharedMemoryCreate
108  *
109  * Create a shared memory segment of the given size and initialize its
110  * standard header.
111  *
112  * makePrivate means to always create a new segment, rather than attach to
113  * or recycle any existing segment. On win32, we always create a new segment,
114  * since there is no need for recycling (segments go away automatically
115  * when the last backend exits)
116  *
117  */
118 PGShmemHeader *
119 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
120 {
121         void       *memAddress;
122         PGShmemHeader *hdr;
123         HANDLE          hmap,
124                                 hmap2;
125         char       *szShareMem;
126         int                     i;
127
128         /* Room for a header? */
129         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
130
131         szShareMem = GetSharedMemName();
132
133         UsedShmemSegAddr = NULL;
134
135         /*
136          * When recycling a shared memory segment, it may take a short while
137          * before it gets dropped from the global namespace. So re-try after
138          * sleeping for a second, and continue retrying 10 times.
139          * (both the 1 second time and the 10 retries are completely arbitrary)
140          */
141         for (i = 0; i < 10; i++)
142         {
143                 /* In case CreateFileMapping() doesn't set the error code to 0 on success */
144                 SetLastError(0);
145
146                 hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF,           /* Use the pagefile */
147                                                                  NULL,          /* Default security attrs */
148                                                                  PAGE_READWRITE,        /* Memory is Read/Write */
149                                                                  0L,    /* Size Upper 32 Bits   */
150                                                                  (DWORD) size,          /* Size Lower 32 bits */
151                                                                  szShareMem);
152
153                 if (!hmap)
154                         ereport(FATAL,
155                                         (errmsg("could not create shared memory segment: %lu", GetLastError()),
156                                          errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
157                                                            (unsigned long) size, szShareMem)));
158
159                 /*
160                  * If the segment already existed, CreateFileMapping() will return a
161                  * handle to the existing one.
162                  */
163                 if (GetLastError() == ERROR_ALREADY_EXISTS)
164                 {
165                         CloseHandle(hmap);              /* Close the old handle, since we got a valid
166                                                                          * one to the previous segment. */
167                         Sleep(1000);
168                         continue;
169                 }
170                 break;
171         }
172
173         /*
174          * If the last call in the loop still returned ERROR_ALREADY_EXISTS, this shared memory
175          * segment exists and we assume it belongs to somebody else.
176          */
177         if (GetLastError() == ERROR_ALREADY_EXISTS)
178                 ereport(FATAL,
179                          (errmsg("pre-existing shared memory block is still in use"),
180                           errhint("Check if there are any old server processes still running, and terminate them.")));
181
182         free(szShareMem);
183
184         /*
185          * Make the handle inheritable
186          */
187         if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
188                 ereport(FATAL,
189                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
190                                  errdetail("Failed system call was DuplicateHandle.")));
191
192         /*
193          * Close the old, non-inheritable handle. If this fails we don't really
194          * care.
195          */
196         if (!CloseHandle(hmap))
197                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
198
199
200         /* Register on-exit routine to delete the new segment */
201         on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2));
202
203         /*
204          * Get a pointer to the new shared memory segment. Map the whole segment
205          * at once, and let the system decide on the initial address.
206          */
207         memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
208         if (!memAddress)
209                 ereport(FATAL,
210                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
211                                  errdetail("Failed system call was MapViewOfFileEx.")));
212
213
214
215         /*
216          * OK, we created a new segment.  Mark it as created by this process. The
217          * order of assignments here is critical so that another Postgres process
218          * can't see the header as valid but belonging to an invalid PID!
219          */
220         hdr = (PGShmemHeader *) memAddress;
221         hdr->creatorPID = getpid();
222         hdr->magic = PGShmemMagic;
223
224         /*
225          * Initialize space allocation status for segment.
226          */
227         hdr->totalsize = size;
228         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
229
230         /* Save info for possible future use */
231         UsedShmemSegAddr = memAddress;
232         UsedShmemSegID = (unsigned long) hmap2;
233
234         return hdr;
235 }
236
237 /*
238  * PGSharedMemoryReAttach
239  *
240  * Re-attach to an already existing shared memory segment. Use the
241  * handle inherited from the postmaster.
242  *
243  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
244  * routine.  The caller must have already restored them to the postmaster's
245  * values.
246  */
247 void
248 PGSharedMemoryReAttach(void)
249 {
250         PGShmemHeader *hdr;
251         void       *origUsedShmemSegAddr = UsedShmemSegAddr;
252
253         Assert(UsedShmemSegAddr != NULL);
254         Assert(IsUnderPostmaster);
255
256         hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
257         if (!hdr)
258                 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
259                          (int) UsedShmemSegID, UsedShmemSegAddr, GetLastError());
260         if (hdr != origUsedShmemSegAddr)
261                 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
262                          hdr, origUsedShmemSegAddr);
263         if (hdr->magic != PGShmemMagic)
264                 elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");
265
266         UsedShmemSegAddr = hdr;         /* probably redundant */
267 }
268
269 /*
270  * PGSharedMemoryDetach
271  *
272  * Detach from the shared memory segment, if still attached.  This is not
273  * intended for use by the process that originally created the segment. Rather,
274  * this is for subprocesses that have inherited an attachment and want to
275  * get rid of it.
276  */
277 void
278 PGSharedMemoryDetach(void)
279 {
280         if (UsedShmemSegAddr != NULL)
281         {
282                 if (!UnmapViewOfFile(UsedShmemSegAddr))
283                         elog(LOG, "could not unmap view of shared memory: %lu", GetLastError());
284
285                 UsedShmemSegAddr = NULL;
286         }
287 }
288
289
290 /*
291  *      pgwin32_SharedMemoryDelete(status, shmId)               deletes a shared memory segment
292  *      (called as an on_shmem_exit callback, hence funny argument list)
293  */
294 static void
295 pgwin32_SharedMemoryDelete(int status, Datum shmId)
296 {
297         PGSharedMemoryDetach();
298         if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
299                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
300 }