Header And Logo

PostgreSQL
| The world's most advanced open source database.

win32_shmem.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * win32_shmem.c
00004  *    Implement shared memory using win32 facilities
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *    src/backend/port/win32_shmem.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 #include "postgres.h"
00014 
00015 #include "miscadmin.h"
00016 #include "storage/ipc.h"
00017 #include "storage/pg_shmem.h"
00018 
00019 HANDLE      UsedShmemSegID = 0;
00020 void       *UsedShmemSegAddr = NULL;
00021 static Size UsedShmemSegSize = 0;
00022 
00023 static void pgwin32_SharedMemoryDelete(int status, Datum shmId);
00024 
00025 /*
00026  * Generate shared memory segment name. Expand the data directory, to generate
00027  * an identifier unique for this data directory. Then replace all backslashes
00028  * with forward slashes, since backslashes aren't permitted in global object names.
00029  *
00030  * Store the shared memory segment in the Global\ namespace (requires NT2 TSE or
00031  * 2000, but that's all we support for other reasons as well), to make sure you can't
00032  * open two postmasters in different sessions against the same data directory.
00033  *
00034  * XXX: What happens with junctions? It's only someone breaking things on purpose,
00035  *      and this is still better than before, but we might want to do something about
00036  *      that sometime in the future.
00037  */
00038 static char *
00039 GetSharedMemName(void)
00040 {
00041     char       *retptr;
00042     DWORD       bufsize;
00043     DWORD       r;
00044     char       *cp;
00045 
00046     bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
00047     if (bufsize == 0)
00048         elog(FATAL, "could not get size for full pathname of datadir %s: error code %lu",
00049              DataDir, GetLastError());
00050 
00051     retptr = malloc(bufsize + 18);      /* 18 for Global\PostgreSQL: */
00052     if (retptr == NULL)
00053         elog(FATAL, "could not allocate memory for shared memory name");
00054 
00055     strcpy(retptr, "Global\\PostgreSQL:");
00056     r = GetFullPathName(DataDir, bufsize, retptr + 18, NULL);
00057     if (r == 0 || r > bufsize)
00058         elog(FATAL, "could not generate full pathname for datadir %s: error code %lu",
00059              DataDir, GetLastError());
00060 
00061     /*
00062      * XXX: Intentionally overwriting the Global\ part here. This was not the
00063      * original approach, but putting it in the actual Global\ namespace
00064      * causes permission errors in a lot of cases, so we leave it in the
00065      * default namespace for now.
00066      */
00067     for (cp = retptr; *cp; cp++)
00068         if (*cp == '\\')
00069             *cp = '/';
00070 
00071     return retptr;
00072 }
00073 
00074 
00075 /*
00076  * PGSharedMemoryIsInUse
00077  *
00078  * Is a previously-existing shmem segment still existing and in use?
00079  *
00080  * The point of this exercise is to detect the case where a prior postmaster
00081  * crashed, but it left child backends that are still running.  Therefore
00082  * we only care about shmem segments that are associated with the intended
00083  * DataDir.  This is an important consideration since accidental matches of
00084  * shmem segment IDs are reasonably common.
00085  *
00086  */
00087 bool
00088 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
00089 {
00090     char       *szShareMem;
00091     HANDLE      hmap;
00092 
00093     szShareMem = GetSharedMemName();
00094 
00095     hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);
00096 
00097     free(szShareMem);
00098 
00099     if (hmap == NULL)
00100         return false;
00101 
00102     CloseHandle(hmap);
00103     return true;
00104 }
00105 
00106 
00107 /*
00108  * PGSharedMemoryCreate
00109  *
00110  * Create a shared memory segment of the given size and initialize its
00111  * standard header.
00112  *
00113  * makePrivate means to always create a new segment, rather than attach to
00114  * or recycle any existing segment. On win32, we always create a new segment,
00115  * since there is no need for recycling (segments go away automatically
00116  * when the last backend exits)
00117  *
00118  */
00119 PGShmemHeader *
00120 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
00121 {
00122     void       *memAddress;
00123     PGShmemHeader *hdr;
00124     HANDLE      hmap,
00125                 hmap2;
00126     char       *szShareMem;
00127     int         i;
00128     DWORD       size_high;
00129     DWORD       size_low;
00130 
00131     /* Room for a header? */
00132     Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
00133 
00134     szShareMem = GetSharedMemName();
00135 
00136     UsedShmemSegAddr = NULL;
00137 
00138 #ifdef _WIN64
00139     size_high = size >> 32;
00140 #else
00141     size_high = 0;
00142 #endif
00143     size_low = (DWORD) size;
00144 
00145     /*
00146      * When recycling a shared memory segment, it may take a short while
00147      * before it gets dropped from the global namespace. So re-try after
00148      * sleeping for a second, and continue retrying 10 times. (both the 1
00149      * second time and the 10 retries are completely arbitrary)
00150      */
00151     for (i = 0; i < 10; i++)
00152     {
00153         /*
00154          * In case CreateFileMapping() doesn't set the error code to 0 on
00155          * success
00156          */
00157         SetLastError(0);
00158 
00159         hmap = CreateFileMapping(INVALID_HANDLE_VALUE,  /* Use the pagefile */
00160                                  NULL,  /* Default security attrs */
00161                                  PAGE_READWRITE,        /* Memory is Read/Write */
00162                                  size_high,     /* Size Upper 32 Bits   */
00163                                  size_low,      /* Size Lower 32 bits */
00164                                  szShareMem);
00165 
00166         if (!hmap)
00167             ereport(FATAL,
00168                     (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
00169                      errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s).",
00170                                (unsigned long) size, szShareMem)));
00171 
00172         /*
00173          * If the segment already existed, CreateFileMapping() will return a
00174          * handle to the existing one and set ERROR_ALREADY_EXISTS.
00175          */
00176         if (GetLastError() == ERROR_ALREADY_EXISTS)
00177         {
00178             CloseHandle(hmap);  /* Close the handle, since we got a valid one
00179                                  * to the previous segment. */
00180             hmap = NULL;
00181             Sleep(1000);
00182             continue;
00183         }
00184         break;
00185     }
00186 
00187     /*
00188      * If the last call in the loop still returned ERROR_ALREADY_EXISTS, this
00189      * shared memory segment exists and we assume it belongs to somebody else.
00190      */
00191     if (!hmap)
00192         ereport(FATAL,
00193                 (errmsg("pre-existing shared memory block is still in use"),
00194                  errhint("Check if there are any old server processes still running, and terminate them.")));
00195 
00196     free(szShareMem);
00197 
00198     /*
00199      * Make the handle inheritable
00200      */
00201     if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
00202         ereport(FATAL,
00203                 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
00204                  errdetail("Failed system call was DuplicateHandle.")));
00205 
00206     /*
00207      * Close the old, non-inheritable handle. If this fails we don't really
00208      * care.
00209      */
00210     if (!CloseHandle(hmap))
00211         elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
00212 
00213 
00214     /* Register on-exit routine to delete the new segment */
00215     on_shmem_exit(pgwin32_SharedMemoryDelete, PointerGetDatum(hmap2));
00216 
00217     /*
00218      * Get a pointer to the new shared memory segment. Map the whole segment
00219      * at once, and let the system decide on the initial address.
00220      */
00221     memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
00222     if (!memAddress)
00223         ereport(FATAL,
00224                 (errmsg("could not create shared memory segment: error code %lu", GetLastError()),
00225                  errdetail("Failed system call was MapViewOfFileEx.")));
00226 
00227 
00228 
00229     /*
00230      * OK, we created a new segment.  Mark it as created by this process. The
00231      * order of assignments here is critical so that another Postgres process
00232      * can't see the header as valid but belonging to an invalid PID!
00233      */
00234     hdr = (PGShmemHeader *) memAddress;
00235     hdr->creatorPID = getpid();
00236     hdr->magic = PGShmemMagic;
00237 
00238     /*
00239      * Initialize space allocation status for segment.
00240      */
00241     hdr->totalsize = size;
00242     hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
00243 
00244     /* Save info for possible future use */
00245     UsedShmemSegAddr = memAddress;
00246     UsedShmemSegSize = size;
00247     UsedShmemSegID = hmap2;
00248 
00249     return hdr;
00250 }
00251 
00252 /*
00253  * PGSharedMemoryReAttach
00254  *
00255  * Re-attach to an already existing shared memory segment. Use the
00256  * handle inherited from the postmaster.
00257  *
00258  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
00259  * routine.  The caller must have already restored them to the postmaster's
00260  * values.
00261  */
00262 void
00263 PGSharedMemoryReAttach(void)
00264 {
00265     PGShmemHeader *hdr;
00266     void       *origUsedShmemSegAddr = UsedShmemSegAddr;
00267 
00268     Assert(UsedShmemSegAddr != NULL);
00269     Assert(IsUnderPostmaster);
00270 
00271     /*
00272      * Release memory region reservation that was made by the postmaster
00273      */
00274     if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0)
00275         elog(FATAL, "failed to release reserved memory region (addr=%p): error code %lu",
00276              UsedShmemSegAddr, GetLastError());
00277 
00278     hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
00279     if (!hdr)
00280         elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu",
00281              UsedShmemSegID, UsedShmemSegAddr, GetLastError());
00282     if (hdr != origUsedShmemSegAddr)
00283         elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
00284              hdr, origUsedShmemSegAddr);
00285     if (hdr->magic != PGShmemMagic)
00286         elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");
00287 
00288     UsedShmemSegAddr = hdr;     /* probably redundant */
00289 }
00290 
00291 /*
00292  * PGSharedMemoryDetach
00293  *
00294  * Detach from the shared memory segment, if still attached.  This is not
00295  * intended for use by the process that originally created the segment. Rather,
00296  * this is for subprocesses that have inherited an attachment and want to
00297  * get rid of it.
00298  */
00299 void
00300 PGSharedMemoryDetach(void)
00301 {
00302     if (UsedShmemSegAddr != NULL)
00303     {
00304         if (!UnmapViewOfFile(UsedShmemSegAddr))
00305             elog(LOG, "could not unmap view of shared memory: error code %lu", GetLastError());
00306 
00307         UsedShmemSegAddr = NULL;
00308     }
00309 }
00310 
00311 
00312 /*
00313  *  pgwin32_SharedMemoryDelete(status, shmId)       deletes a shared memory segment
00314  *  (called as an on_shmem_exit callback, hence funny argument list)
00315  */
00316 static void
00317 pgwin32_SharedMemoryDelete(int status, Datum shmId)
00318 {
00319     PGSharedMemoryDetach();
00320     if (!CloseHandle(DatumGetPointer(shmId)))
00321         elog(LOG, "could not close handle to shared memory: error code %lu", GetLastError());
00322 }
00323 
00324 /*
00325  * pgwin32_ReserveSharedMemoryRegion(hChild)
00326  *
00327  * Reserve the memory region that will be used for shared memory in a child
00328  * process. It is called before the child process starts, to make sure the
00329  * memory is available.
00330  *
00331  * Once the child starts, DLLs loading in different order or threads getting
00332  * scheduled differently may allocate memory which can conflict with the
00333  * address space we need for our shared memory. By reserving the shared
00334  * memory region before the child starts, and freeing it only just before we
00335  * attempt to get access to the shared memory forces these allocations to
00336  * be given different address ranges that don't conflict.
00337  *
00338  * NOTE! This function executes in the postmaster, and should for this
00339  * reason not use elog(FATAL) since that would take down the postmaster.
00340  */
00341 int
00342 pgwin32_ReserveSharedMemoryRegion(HANDLE hChild)
00343 {
00344     void       *address;
00345 
00346     Assert(UsedShmemSegAddr != NULL);
00347     Assert(UsedShmemSegSize != 0);
00348 
00349     address = VirtualAllocEx(hChild, UsedShmemSegAddr, UsedShmemSegSize,
00350                              MEM_RESERVE, PAGE_READWRITE);
00351     if (address == NULL)
00352     {
00353         /* Don't use FATAL since we're running in the postmaster */
00354         elog(LOG, "could not reserve shared memory region (addr=%p) for child %p: error code %lu",
00355              UsedShmemSegAddr, hChild, GetLastError());
00356         return false;
00357     }
00358     if (address != UsedShmemSegAddr)
00359     {
00360         /*
00361          * Should never happen - in theory if allocation granularity causes
00362          * strange effects it could, so check just in case.
00363          *
00364          * Don't use FATAL since we're running in the postmaster.
00365          */
00366         elog(LOG, "reserved shared memory region got incorrect address %p, expected %p",
00367              address, UsedShmemSegAddr);
00368         VirtualFreeEx(hChild, address, 0, MEM_RELEASE);
00369         return false;
00370     }
00371 
00372     return true;
00373 }