On Tue, Mar 20, 2007 at 07:41:32AM +0100, Magnus Hagander wrote:
> > > Does it seem like I've overlooked anything obvious in this? I do get the
> > > feeling that this is too simple, but I don't know exactly where the
> > > problem is :-)
> > 
> > I think you do still need the on_shmem_exit detach callback.  Consider
> > the situation where the postmaster is trying to reinitialize after a
> > child crash.  The Unix code is designed to detach and destroy the old
> > segment then create a new one.  If that's not what you want to do then
> > this code still seems not right.
> 
> Ok, will look into that. Haven't tested that scenario.

That was indeed so. Added in new version, attached.

> > There seem to be a lot of system calls not checked for failure here.
> > Do they really not have any failure possibilities?

I looked it over, and didn't find "a lot". I found one or two (which are
now fixed). Are you referring to anything in particular?

//Magnus

/*-------------------------------------------------------------------------
 *
 * win32_shmem.c
 *	  Implement shared memory using win32 facilities
 *
 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  $PostgreSQL$
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"

unsigned long UsedShmemSegID = 0;
void	   *UsedShmemSegAddr = NULL;

static void pgwin32_SharedMemoryDelete(int status, Datum shmId);

/*
 * Generate shared memory segment name. Expand the data directory, to generate
 * an identifier unique for this data directory. Then replace all backslashes
 * with forward slashes, since backslashes aren't permitted in global object names.
 *
 * XXX: What happens with junctions? It's only someone breaking things on purpose,
 *      and this is still better than before, but we might want to do something about
 *      that sometime in the future.
 */
static char *
GetSharedMemName(void)
{
	char	   *retptr;
	DWORD		bufsize;
	DWORD		r;
	char	   *cp;

	bufsize = GetFullPathName(DataDir, 0, NULL, NULL);
	if (bufsize == 0)
		elog(FATAL, "could not get size for full pathname of datadir %s: %lu",
			DataDir, GetLastError());

	retptr = malloc(bufsize+1+11); // 1 NULL and 11 for PostgreSQL
	if (retptr == NULL)
		elog(FATAL, "could not allocate memory for shared memory name");

	strcpy(retptr,"PostgreSQL:");
	r = GetFullPathName(DataDir, bufsize, retptr+11, NULL);
	if (r == 0 || r > bufsize)
		elog(FATAL, "could not generate full pathname for datadir %s: %lu",
			DataDir, GetLastError());

	for (cp = retptr; *cp; cp++)
		if (*cp == '\\')
			*cp = '/';

	return retptr;
}


/*
 * PGSharedMemoryIsInUse
 *
 * Is a previously-existing shmem segment still existing and in use?
 *
 * The point of this exercise is to detect the case where a prior postmaster
 * crashed, but it left child backends that are still running.	Therefore
 * we only care about shmem segments that are associated with the intended
 * DataDir.  This is an important consideration since accidental matches of
 * shmem segment IDs are reasonably common.
 *
 */
bool
PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
{
	char	   *szShareMem;
	HANDLE		hmap;

	szShareMem = GetSharedMemName();

	hmap = OpenFileMapping(FILE_MAP_READ, FALSE, szShareMem);

	free(szShareMem);

	if (hmap == NULL)
		return false;

	CloseHandle(hmap);
	return true;
}


/*
 * PGSharedMemoryCreate
 *
 * Create a shared memory segment of the given size and initialize its
 * standard header.  
 *
 * makePrivate means to always create a new segment, rather than attach to
 * or recycle any existing segment. On win32, we always create a new segment,
 * since there is no need for recycling (segments go away automatically
 * when the last backend exits)
 *
 */
PGShmemHeader *
PGSharedMemoryCreate(Size size, bool makePrivate, int port)
{
	void		   *memAddress;
	PGShmemHeader  *hdr;
	HANDLE			hmap, hmap2;
	char		   *szShareMem;

	/* Room for a header? */
	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));

	szShareMem = GetSharedMemName();

	UsedShmemSegAddr = NULL;

	hmap = CreateFileMapping((HANDLE) 0xFFFFFFFF,	/* Use the pagefile */
							 NULL,					/* Default security attrs */
							 PAGE_READWRITE,		/* Memory is Read/Write */
							 0L,					/* Size Upper 32 Bits	*/
							 (DWORD) size,			/* Size Lower 32 bits */
							 szShareMem);

	if (!hmap)
		ereport(FATAL,
				(errmsg("could not create shared memory segment: %lu", GetLastError()),
				errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s)", size, szShareMem)));

	/*
	 * If the segment already existed, CreateFileMapping() will return a handle to the
	 * existing one.
	 */
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		/*
		 * When recycling a shared memory segment, it may take a short while
		 * before it gets dropped from the global namespace. So re-try after
		 * sleeping for a second.
		 */
		CloseHandle(hmap); /* Close the old handle, since we got a valid one to the previous segment. */

		Sleep(1000);

		hmap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0L, (DWORD) size, szShareMem);
		if (!hmap)
			ereport(FATAL,
					(errmsg("could not create shared memory segment: %lu", GetLastError()),
					errdetail("Failed system call was CreateFileMapping(size=%lu, name=%s)", size, szShareMem)));

		if (GetLastError() == ERROR_ALREADY_EXISTS)
			ereport(FATAL,
					 (errmsg("pre-existing shared memory block is still in use"),
					 errhint("Check if there are any old server processes still running, and terminate them.")));
	}

	free(szShareMem);

	/*
	 * Make the handle inheritable
	 */
	if (!DuplicateHandle(GetCurrentProcess(), hmap, GetCurrentProcess(), &hmap2, 0, TRUE, DUPLICATE_SAME_ACCESS))
		ereport(FATAL,
				(errmsg("could not create shared memory segment: %lu", GetLastError()),
				errdetail("Failed system call was DuplicateHandle")));

	/*
	 * Close the old, non-inheritable handle. If this fails we don't really care.
	 */
	if (!CloseHandle(hmap))
		elog(LOG, "could not close handle to shared memory: %lu", GetLastError());


	/* Register on-exit routine to delete the new segment */
	on_shmem_exit(pgwin32_SharedMemoryDelete, Int32GetDatum((unsigned long) hmap2));

	/*
	 * Get a pointer to the new shared memory segment. Map the whole segment at once,
	 * and let the system decide on the initial address.
	 */
	memAddress = MapViewOfFileEx(hmap2, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0, NULL);
	if (!memAddress)
		ereport(FATAL,
				(errmsg("could not create shared memory segment: %lu", GetLastError()),
				errdetail("Failed system call was MapViewOfFileEx")));


	
	/*
	 * OK, we created a new segment.  Mark it as created by this process. The
	 * order of assignments here is critical so that another Postgres process
	 * can't see the header as valid but belonging to an invalid PID!
	 */
	hdr = (PGShmemHeader *) memAddress;
	hdr->creatorPID = getpid();
	hdr->magic = PGShmemMagic;

	/*
	 * Initialize space allocation status for segment.
	 */
	hdr->totalsize = size;
	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));

	/* Save info for possible future use */
	UsedShmemSegAddr = memAddress;
	UsedShmemSegID = (unsigned long) hmap2;

	return hdr;
}

/*
 * PGSharedMemoryReAttach
 *
 * Re-attach to an already existing shared memory segment. Use the 
 * handle inherited from the postmaster.
 *
 * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
 * routine.  The caller must have already restored them to the postmaster's
 * values.
 */
void
PGSharedMemoryReAttach(void)
{
	PGShmemHeader	   *hdr;
	void			   *origUsedShmemSegAddr = UsedShmemSegAddr;

	Assert(UsedShmemSegAddr != NULL);
	Assert(IsUnderPostmaster);

	hdr = (PGShmemHeader *)MapViewOfFileEx((HANDLE)UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr);
	if (!hdr)
		elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %lu",
			 (int) UsedShmemSegID, UsedShmemSegAddr, GetLastError());
	if (hdr != origUsedShmemSegAddr)
		elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
			 hdr, origUsedShmemSegAddr);
	if (hdr->magic != PGShmemMagic)
		elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory");

	UsedShmemSegAddr = hdr;		/* probably redundant */
}

/*
 * PGSharedMemoryDetach
 *
 * Detach from the shared memory segment, if still attached.  This is not
 * intended for use by the process that originally created the segment. Rather,
 * this is for subprocesses that have inherited an attachment and want to
 * get rid of it.
 */
void
PGSharedMemoryDetach(void)
{
	if (UsedShmemSegAddr != NULL)
	{
		if (!UnmapViewOfFile(UsedShmemSegAddr))
			elog(LOG, "could not unmap view of shared memory: %lu", GetLastError());

		UsedShmemSegAddr = NULL;
	}
}


/*
 *	pgwin32_SharedMemoryDelete(status, shmId)		deletes a shared memory segment
 *	(called as an on_shmem_exit callback, hence funny argument list)
 */
static void
pgwin32_SharedMemoryDelete(int status, Datum shmId)
{
	PGSharedMemoryDetach();
	if (!CloseHandle((HANDLE)DatumGetInt32(shmId)))
		elog(LOG, "could not close handle to shared memory: %lu", GetLastError());
}
---------------------------(end of broadcast)---------------------------
TIP 6: explain analyze is your friend

Reply via email to