@@ -54,6 +54,7 @@ MemoryContext CurTransactionContext = NULL;
5454/* This is a transient link to the active portal's memory context: */
5555MemoryContext PortalContext = NULL ;
5656
57+ static void MemoryContextCallResetCallbacks (MemoryContext context );
5758static void MemoryContextStatsInternal (MemoryContext context , int level );
5859
5960/*
@@ -115,9 +116,8 @@ MemoryContextInit(void)
115116 * where retained memory in a context is *essential* --- we want to be
116117 * sure ErrorContext still has some memory even if we've run out
117118 * elsewhere! Also, allow allocations in ErrorContext within a critical
118- * section. Otherwise a PANIC will cause an assertion failure in the
119- * error reporting code, before printing out the real cause of the
120- * failure.
119+ * section. Otherwise a PANIC will cause an assertion failure in the error
120+ * reporting code, before printing out the real cause of the failure.
121121 *
122122 * This should be the last step in this function, as elog.c assumes memory
123123 * management works once ErrorContext is non-null.
@@ -150,6 +150,7 @@ MemoryContextReset(MemoryContext context)
150150 /* Nothing to do if no pallocs since startup or last reset */
151151 if (!context -> isReset )
152152 {
153+ MemoryContextCallResetCallbacks (context );
153154 (* context -> methods -> reset ) (context );
154155 context -> isReset = true;
155156 VALGRIND_DESTROY_MEMPOOL (context );
@@ -195,6 +196,14 @@ MemoryContextDelete(MemoryContext context)
195196
196197 MemoryContextDeleteChildren (context );
197198
199+ /*
200+ * It's not entirely clear whether 'tis better to do this before or after
201+ * delinking the context; but an error in a callback will likely result in
202+ * leaking the whole context (if it's not a root context) if we do it
203+ * after, so let's do it before.
204+ */
205+ MemoryContextCallResetCallbacks (context );
206+
198207 /*
199208 * We delink the context from its parent before deleting it, so that if
200209 * there's an error we won't have deleted/busted contexts still attached
@@ -242,6 +251,56 @@ MemoryContextResetAndDeleteChildren(MemoryContext context)
242251 MemoryContextReset (context );
243252}
244253
254+ /*
255+ * MemoryContextRegisterResetCallback
256+ * Register a function to be called before next context reset/delete.
257+ * Such callbacks will be called in reverse order of registration.
258+ *
259+ * The caller is responsible for allocating a MemoryContextCallback struct
260+ * to hold the info about this callback request, and for filling in the
261+ * "func" and "arg" fields in the struct to show what function to call with
262+ * what argument. Typically the callback struct should be allocated within
263+ * the specified context, since that means it will automatically be freed
264+ * when no longer needed.
265+ *
266+ * There is no API for deregistering a callback once registered. If you
267+ * want it to not do anything anymore, adjust the state pointed to by its
268+ * "arg" to indicate that.
269+ */
270+ void
271+ MemoryContextRegisterResetCallback (MemoryContext context ,
272+ MemoryContextCallback * cb )
273+ {
274+ AssertArg (MemoryContextIsValid (context ));
275+
276+ /* Push onto head so this will be called before older registrants. */
277+ cb -> next = context -> reset_cbs ;
278+ context -> reset_cbs = cb ;
279+ /* Mark the context as non-reset (it probably is already). */
280+ context -> isReset = false;
281+ }
282+
283+ /*
284+ * MemoryContextCallResetCallbacks
285+ * Internal function to call all registered callbacks for context.
286+ */
287+ static void
288+ MemoryContextCallResetCallbacks (MemoryContext context )
289+ {
290+ MemoryContextCallback * cb ;
291+
292+ /*
293+ * We pop each callback from the list before calling. That way, if an
294+ * error occurs inside the callback, we won't try to call it a second time
295+ * in the likely event that we reset or delete the context later.
296+ */
297+ while ((cb = context -> reset_cbs ) != NULL )
298+ {
299+ context -> reset_cbs = cb -> next ;
300+ (* cb -> func ) (cb -> arg );
301+ }
302+ }
303+
245304/*
246305 * MemoryContextSetParent
247306 * Change a context to belong to a new parent (or no parent).
318377MemoryContextAllowInCriticalSection (MemoryContext context , bool allow )
319378{
320379 AssertArg (MemoryContextIsValid (context ));
321- #ifdef USE_ASSERT_CHECKING
380+
322381 context -> allowInCritSection = allow ;
323- #endif
324382}
325383
326384/*
@@ -589,11 +647,8 @@ MemoryContextCreate(NodeTag tag, Size size,
589647 node -> parent = parent ;
590648 node -> nextchild = parent -> firstchild ;
591649 parent -> firstchild = node ;
592-
593- #ifdef USE_ASSERT_CHECKING
594650 /* inherit allowInCritSection flag from parent */
595651 node -> allowInCritSection = parent -> allowInCritSection ;
596- #endif
597652 }
598653
599654 VALGRIND_CREATE_MEMPOOL (node , 0 , false);
0 commit comments