]> granicus.if.org Git - postgresql/blob - src/backend/port/win32_shmem.c
Add sentence-ending periods.
[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-2007, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/port/win32_shmem.c,v 1.3 2007/11/08 14:47:41 petere 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 + 1 + 18);      /* 1 NULL and 18 for
51                                                                                  * 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 + 11, NULL);
57         if (r == 0 || r > bufsize)
58                 elog(FATAL, "could not generate full pathname for datadir %s: %lu",
59                          DataDir, GetLastError());
60
61         for (cp = retptr; *cp; cp++)
62                 if (*cp == '\\')
63                         *cp = '/';
64
65         return retptr;
66 }
67
68
69 /*
70  * PGSharedMemoryIsInUse
71  *
72  * Is a previously-existing shmem segment still existing and in use?
73  *
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.
79  *
80  */
81 bool
82 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
83 {
84         char       *szShareMem;
85         HANDLE          hmap;
86
87         szShareMem = GetSharedMemName();
88
89         hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
90
91         free(szShareMem);
92
93         if (hmap == NULL)
94                 return false;
95
96         CloseHandle(hmap);
97         return true;
98 }
99
100
101 /*
102  * PGSharedMemoryCreate
103  *
104  * Create a shared memory segment of the given size and initialize its
105  * standard header.
106  *
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)
111  *
112  */
113 PGShmemHeader *
114 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
115 {
116         void       *memAddress;
117         PGShmemHeader *hdr;
118         HANDLE          hmap,
119                                 hmap2;
120         char       *szShareMem;
121
122         /* Room for a header? */
123         Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
124
125         szShareMem = GetSharedMemName();
126
127         UsedShmemSegAddr = NULL;
128
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 */
134                                                          szShareMem);
135
136         if (!hmap)
137                 ereport(FATAL,
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)));
141
142         /*
143          * If the segment already existed, CreateFileMapping() will return a
144          * handle to the existing one.
145          */
146         if (GetLastError() == ERROR_ALREADY_EXISTS)
147         {
148                 /*
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.
152                  */
153                 CloseHandle(hmap);              /* Close the old handle, since we got a valid
154                                                                  * one to the previous segment. */
155
156                 Sleep(1000);
157
158                 hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0L, (DWORD) size, szShareMem);
159                 if (!hmap)
160                         ereport(FATAL,
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)));
164
165                 if (GetLastError() == ERROR_ALREADY_EXISTS)
166                         ereport(FATAL,
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.")));
169         }
170
171         free(szShareMem);
172
173         /*
174          * Make the handle inheritable
175          */
176         if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
177                 ereport(FATAL,
178                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
179                                  errdetail("Failed system call was DuplicateHandle.")));
180
181         /*
182          * Close the old, non-inheritable handle. If this fails we don't really
183          * care.
184          */
185         if (!CloseHandle(hmap))
186                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
187
188
189         /* Register on-exit routine to delete the new segment */
190         on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2));
191
192         /*
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.
195          */
196         memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
197         if (!memAddress)
198                 ereport(FATAL,
199                                 (errmsg("could not create shared memory segment: %lu", GetLastError()),
200                                  errdetail("Failed system call was MapViewOfFileEx.")));
201
202
203
204         /*
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!
208          */
209         hdr = (PGShmemHeader *) memAddress;
210         hdr->creatorPID = getpid();
211         hdr->magic = PGShmemMagic;
212
213         /*
214          * Initialize space allocation status for segment.
215          */
216         hdr->totalsize = size;
217         hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
218
219         /* Save info for possible future use */
220         UsedShmemSegAddr = memAddress;
221         UsedShmemSegID = (unsigned long) hmap2;
222
223         return hdr;
224 }
225
226 /*
227  * PGSharedMemoryReAttach
228  *
229  * Re-attach to an already existing shared memory segment. Use the
230  * handle inherited from the postmaster.
231  *
232  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
233  * routine.  The caller must have already restored them to the postmaster's
234  * values.
235  */
236 void
237 PGSharedMemoryReAttach(void)
238 {
239         PGShmemHeader *hdr;
240         void       *origUsedShmemSegAddr = UsedShmemSegAddr;
241
242         Assert(UsedShmemSegAddr != NULL);
243         Assert(IsUnderPostmaster);
244
245         hdr = (PGShmemHeader *) MapViewOfFileEx((HANDLE) UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
246         if (!hdr)
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");
254
255         UsedShmemSegAddr = hdr;         /* probably redundant */
256 }
257
258 /*
259  * PGSharedMemoryDetach
260  *
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
264  * get rid of it.
265  */
266 void
267 PGSharedMemoryDetach(void)
268 {
269         if (UsedShmemSegAddr != NULL)
270         {
271                 if (!UnmapViewOfFile(UsedShmemSegAddr))
272                         elog(LOG, "could not unmap view of shared memory: %lu", GetLastError());
273
274                 UsedShmemSegAddr = NULL;
275         }
276 }
277
278
279 /*
280  *      pgwin32_SharedMemoryDelete(status, shmId)               deletes a shared memory segment
281  *      (called as an on_shmem_exit callback, hence funny argument list)
282  */
283 static void
284 pgwin32_SharedMemoryDelete(int status, Datum shmId)
285 {
286         PGSharedMemoryDetach();
287         if (!CloseHandle((HANDLE) DatumGetInt32(shmId)))
288                 elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
289 }