From 8c16841ed93a100e8a4ec25f78bc610ffcdb4f84 Mon Sep 17 00:00:00 2001
From: "ideriha.takeshi" <ideriha.takeshi@jp.fujitsu.com>
Date: Tue, 8 Oct 2019 10:54:06 +0900
Subject: [PATCH 1/2]  MemoryContext for shared memory based on DSA.     This
 is simplified version and just dsa_allocate and dsa_free     via
 palloc/pfree. It doesn't solve memory leak,     which is under discussion:   
 
 https://www.postgresql.org/message-id/4E72940DA2BF16479384A86D54D0988A6F1EE452%40G01JPEXMBKW04

---
 src/backend/utils/mmgr/Makefile   |   3 +-
 src/backend/utils/mmgr/shm_mcxt.c | 302 ++++++++++++++++++++++++++++++++++++++
 src/include/nodes/memnodes.h      |   3 +-
 src/include/nodes/nodes.h         |   1 +
 src/include/utils/memutils.h      |   9 +-
 5 files changed, 315 insertions(+), 3 deletions(-)
 create mode 100644 src/backend/utils/mmgr/shm_mcxt.c

diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index f644c40..37134b8 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -12,6 +12,7 @@ subdir = src/backend/utils/mmgr
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = aset.o dsa.o freepage.o generation.o mcxt.o memdebug.o portalmem.o slab.o
+OBJS = aset.o dsa.o freepage.o generation.o mcxt.o memdebug.o portalmem.o slab.o \
+	 shm_mcxt.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/mmgr/shm_mcxt.c b/src/backend/utils/mmgr/shm_mcxt.c
new file mode 100644
index 0000000..4ba1052
--- /dev/null
+++ b/src/backend/utils/mmgr/shm_mcxt.c
@@ -0,0 +1,302 @@
+/*-------------------------------------------------------------------------
+ *
+ * shm_mcxt.c
+ *	  ShmContext allocator definitions.
+ *
+ * ShmContext is a MemoryContext implementation designed for cases where you
+ * want to allocate and free objects in the shared memory by MemoryContext
+ * API (palloc/pfree).
+ *
+ * Portions Copyright (c) 2017-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/shm_mcxt.c
+ *
+ *
+ * NOTE:
+ *
+ * ShmContext allows allocation in the shared memory via palloc().
+ * This is intended to migrate locally allocated objects into shared memory.
+ * These objects could be plancache, syscache or somthing else. They usually
+ * allocate memory in local heap by palloc().  * To take advantage of exisiting
+ * code, ShmContext uses dsa_allocate()/dsa_free() as palloc()/pfree().
+ * However, dsa_allocate() returns dsa_pointer while palloc() returns native
+ * pointer. And also an object may be a graph structure with pointers.
+ * It needs to remember either native pointer or dsa_pointer.
+ *
+ * So allow the creation of DSA areas inside the traditional fixed memory
+ * segment (instead of DSM), in a fixed-sized space reserved by the postmaster.
+ * In this case, dsa_pointer is directly castable to a raw pointer, which is
+ * common to every process. This fits to regular MemoryContext interface. But
+ * note that the total size is fixed at start up time.
+ *
+ * Now we can put objects into shared memory via palloc(). But without
+ * some garbage collection mechanism, memory leaks will be cluster-wide
+ * and cluster-life-time. Leaked object won't go away even if one backend
+ * exits.
+ *
+ * To address this issue, ShmContext has two types of context: "temporary" and
+ * "permanent" one. "Temporary" context is located in local regular MemoryContext
+ * and has buffer of dsa_pointers to dsa_allocated objects in order to free them
+ * all at once at transaction rollback. Once developers think memory leak won't
+ * happen, you can re-parent these temp objects to permanent context. Permanent
+ * context exists in the shared memory.
+ *
+ * API to manipulate "temporary" and "permanent" context.
+ * - CreatePermShmContext()
+ * - CreateTempShmContext()
+ * - ChangeToPermShmContext()
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "storage/shmem.h"
+#include "utils/dsa.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+
+
+/*
+ * ShmContext is a specialized memory context supporting dsa area created
+ * above Postmaster-initialized shared memory
+ */
+typedef struct ShmContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+
+	/* ShmContext parameters */
+	void	 *base;	/* raw address of Postmaster-initialized shared memory */
+	dsa_area   *area; /* dsa area created-in-place  */
+} ShmContext;
+
+
+
+#define isTempShmContext(shm_context) (shm_context->temp_buffer != NULL)
+#define isBufferFull(buf) (buf->idx == NUM_CHUNKS)
+#define dsaptr_to_rawptr(dsa_p, base_p)	\
+	((char *)(base_p) + dsa_p)
+#define rawptr_to_dsaptr(raw_p, base_p)	\
+	((dsa_pointer) ((char *)raw_p - (char *)base_p))
+
+
+
+/*
+ * These functions implement the MemoryContext API for ShmContext.
+ */
+static void *ShmContextAlloc(MemoryContext context, Size size);
+static void ShmContextFree(MemoryContext context, void *pointer);
+static void *ShmContextRealloc(MemoryContext context, void *pointer, Size size);
+static void ShmContextReset(MemoryContext context);
+static void ShmContextDelete(MemoryContext context);
+static Size ShmContextGetChunkSpace(MemoryContext context, void *pointer);
+static bool ShmContextIsEmpty(MemoryContext context);
+static void ShmContextStats(MemoryContext context,
+					  MemoryStatsPrintFunc printfunc, void *passthru,
+					  MemoryContextCounters *totals);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void ShmContextCheck(MemoryContext context);
+#endif
+
+/*
+ * This is the virtual function table for ShmContext contexts.
+ */
+static const MemoryContextMethods ShmContextMethods = {
+	ShmContextAlloc,
+	ShmContextFree,
+	ShmContextRealloc,
+	ShmContextReset,
+	ShmContextDelete,
+	ShmContextGetChunkSpace,
+	ShmContextIsEmpty,
+	ShmContextStats
+#ifdef MEMORY_CONTEXT_CHECKING
+	,ShmContextCheck
+#endif
+};
+
+
+/*
+ * CreatePermShmContext
+ *		Create a new permanent ShmContext context.
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * area: dsa_area created in place of Postmaster-initialized shared memory
+ * base: address of Postmaster-initialized shared memory
+ *
+ * This context itself is allocated on shared memory.
+ */
+MemoryContext
+CreatePermShmContext(MemoryContext parent,
+					 const char *name,
+					 dsa_area *area,
+					 void *base)
+{
+	ShmContext *shmContext;
+	bool found;
+
+	shmContext = (ShmContext *)
+		ShmemInitStruct(name, ShmContextSize(), &found);
+
+	if (found)
+		return &shmContext->header;
+
+	MemoryContextCreate(&shmContext->header,
+						T_ShmContext,
+						&ShmContextMethods,
+						parent,
+						name);
+
+	shmContext->base = base;
+	shmContext->area = area;
+
+	return &shmContext->header;
+}
+
+/*
+ * ShmContextSize
+ *		Size of ShmContext
+ * If you use CreatePermShmContext, this fucntion should be called
+ * to reserve the size of ShmContext at postgres start-up time
+ */
+Size
+ShmContextSize(void)
+{
+	return sizeof(ShmContext);
+}
+
+
+
+/*
+ * ShmContextReset
+ * 		Free all the memory registered in temp_buffer and buffer
+ *
+ * The chunks registered in temp_buffer are dsa_freed.
+ * This does not affect permanent context.
+ *
+ */
+static void
+ShmContextReset(MemoryContext context)
+{
+	ShmContext  *shmContext = (ShmContext *) context;
+
+	Assert(shmContext);
+
+	elog(ERROR,
+		 "reset is not supported at permanent DSA MemoryContext");
+}
+
+/*
+ * ShmContextDelete
+ *  	Free all the memory registered in temp_buffer and context.
+ *		See ShmContextReset.
+ */
+static void
+ShmContextDelete(MemoryContext context)
+{
+	/* Reset to release all the temp_buffers */
+	ShmContextReset(context);
+	/* And free the context header */
+	pfree(context);
+}
+
+/*
+ * ShmContextAlloc
+ *		Returns native pointer to allocated memory
+ */
+static void *
+ShmContextAlloc(MemoryContext context, Size size)
+{
+	ShmContext *shmContext = (ShmContext *) context;
+	dsa_pointer chunk_dp;
+	char *chunk_backp;
+
+	chunk_dp = dsa_allocate(shmContext->area, sizeof(void *) + size);
+
+	chunk_backp = dsaptr_to_rawptr(chunk_dp, shmContext->base);
+
+	*(void **) chunk_backp = context;
+
+	return chunk_backp + sizeof(void *);
+}
+
+
+/*
+ * ShmContextFree
+ *		Frees allocated memory
+ */
+static void
+ShmContextFree(MemoryContext context, void *pointer)
+{
+	ShmContext *shmContext = (ShmContext *) context;
+	char *chunk_backp;
+	dsa_pointer dp;
+
+	/* Rewind to the secret start of the chunk */
+	chunk_backp = (char *) pointer - sizeof(void *);
+	dp = rawptr_to_dsaptr(chunk_backp, shmContext->base);
+
+	dsa_free(shmContext->area, dp);
+}
+
+/*
+ * ShmContextRealloc
+ *
+ *	realloc() is not supported
+ */
+static void *
+ShmContextRealloc(MemoryContext context, void *pointer, Size size)
+{
+	elog(ERROR, "ShmContext does not support realloc()");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * ShmContextGetChunkSpace
+ */
+static Size
+ShmContextGetChunkSpace(MemoryContext context, void *pointer)
+{
+	elog(ERROR, "ShmContext does not support get_chunk_space()");
+	return 0;				/* keep compiler quiet */
+}
+
+/*
+ * ShmContextIsEmpty
+ */
+static bool
+ShmContextIsEmpty(MemoryContext context)
+{
+	elog(ERROR, "ShmContext does not support is_empty()");
+	return false;				/* keep compiler quiet */
+}
+
+/*
+ * ShmContextStats
+ *		Compute stats about memory consumption of a ShmContext context.
+ */
+static void
+ShmContextStats(MemoryContext context,
+		  MemoryStatsPrintFunc printfunc, void *passthru,
+		  MemoryContextCounters *totals)
+{
+	elog(ERROR, "ShmContext does not support stats()");
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * ShmContextCheck
+ *
+ * XXX: for now, do nothing
+ */
+static void
+ShmContextCheck(MemoryContext context)
+{
+
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index 854bd5d..ebddcb1 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -103,6 +103,7 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), ShmContext)))
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index bce2d59..a38d95d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -280,6 +280,7 @@ typedef enum NodeTag
 	T_AllocSetContext,
 	T_SlabContext,
 	T_GenerationContext,
+	T_ShmContext,
 
 	/*
 	 * TAGS FOR VALUE NODES (value.h)
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 106c83d..ebbba6c 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -18,7 +18,7 @@
 #define MEMUTILS_H
 
 #include "nodes/memnodes.h"
-
+#include "utils/dsa.h"
 
 /*
  * MaxAllocSize, MaxAllocHugeSize
@@ -182,6 +182,13 @@ extern MemoryContext GenerationContextCreate(MemoryContext parent,
 											 const char *name,
 											 Size blockSize);
 
+/* shm_mcxt.c */
+extern MemoryContext CreatePermShmContext(MemoryContext parent,
+										  const char *name,
+										  dsa_area *area,
+										  void *base);
+extern Size ShmContextSize(void);
+
 /*
  * Recommended default alloc parameters, suitable for "ordinary" contexts
  * that might hold quite a lot of data.
-- 
1.8.3.1

