On Mon, Nov 27, 2017 at 10:25 PM, Thomas Munro
<thomas.mu...@enterprisedb.com> wrote:
> On Thu, Nov 23, 2017 at 12:36 AM, Thomas Munro
> <thomas.mu...@enterprisedb.com> wrote:
>> Here's a new patch set with responses to the last batch of review comments.
>
> Rebased on top of the recent SGML->XML change.

Andres asked me off-list how I tested the barrier.c case where a
backend detaches, releasing other waiters.  There are special cases in
BarrierArriveAndWait() and BarrierDetach() for that to make sure that
the phase advances and waiters are released if they were only waiting
for this one backend to arrive, and that exactly one of them is
"elected" for any serial work.  Normally the last to arrive is elected
(see earlier discussion about why that's a good idea), but if the one
that would be last detaches instead of arriving then we'll have to
elect someone else.  Here is a throw-away test harness that can be
used to exercise that case.  CREATE EXTENSION test_barrier; SELECT
test_barrier_detach_releases(1);.  Adding a sleep before BarrierDetach
can be used to influence the race, and adding elog messages to
barrier.c can be used to see when the special path is taken.

-- 
Thomas Munro
http://www.enterprisedb.com
From 4ba3fe2e318dd1a7b54b3b9ae8853531ed3a6e12 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@enterprisedb.com>
Date: Thu, 30 Nov 2017 14:00:10 +1300
Subject: [PATCH] A simple test module for barrier.c.

Some things to try:

CREATE EXTENSION test_barrier;
SELECT test_barrier_detach_releases(1);
SELECT test_barrier_reattach_random(4, 1000);
SELECT test_barrier_alternate(4, 1000);
---
 src/test/modules/test_barrier/Makefile             |  18 ++
 .../modules/test_barrier/test_barrier--1.0.sql     |  18 ++
 src/test/modules/test_barrier/test_barrier.c       | 270 +++++++++++++++++++++
 src/test/modules/test_barrier/test_barrier.control |   4 +
 4 files changed, 310 insertions(+)
 create mode 100644 src/test/modules/test_barrier/Makefile
 create mode 100644 src/test/modules/test_barrier/test_barrier--1.0.sql
 create mode 100644 src/test/modules/test_barrier/test_barrier.c
 create mode 100644 src/test/modules/test_barrier/test_barrier.control

diff --git a/src/test/modules/test_barrier/Makefile 
b/src/test/modules/test_barrier/Makefile
new file mode 100644
index 00000000000..9f330b6a45d
--- /dev/null
+++ b/src/test/modules/test_barrier/Makefile
@@ -0,0 +1,18 @@
+# src/test/modules/test_barrier/Makefile
+
+MODULES = test_barrier
+
+EXTENSION = test_barrier
+DATA = test_barrier--1.0.sql
+PGFILEDESC = "test_barrier -- some tests for barrier.c"
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_barrier
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_barrier/test_barrier--1.0.sql 
b/src/test/modules/test_barrier/test_barrier--1.0.sql
new file mode 100644
index 00000000000..7077f3f6567
--- /dev/null
+++ b/src/test/modules/test_barrier/test_barrier--1.0.sql
@@ -0,0 +1,18 @@
+\echo Use "CREATE EXTENSION test_barrier" to load this file. \quit
+
+CREATE FUNCTION test_barrier_alternate(workers int, loops int)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE FUNCTION test_barrier_reattach_random(workers int, end_phase int)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE FUNCTION test_barrier_detach_releases(workers int)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+
diff --git a/src/test/modules/test_barrier/test_barrier.c 
b/src/test/modules/test_barrier/test_barrier.c
new file mode 100644
index 00000000000..98adefa809d
--- /dev/null
+++ b/src/test/modules/test_barrier/test_barrier.c
@@ -0,0 +1,270 @@
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "postmaster/bgworker.h"
+#include "storage/barrier.h"
+#include "storage/dsm.h"
+#include "storage/proc.h"
+#include "utils/builtins.h"
+#include "utils/resowner.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_barrier_alternate);
+PG_FUNCTION_INFO_V1(test_barrier_reattach_random);
+PG_FUNCTION_INFO_V1(test_barrier_detach_releases);
+
+extern Datum test_barrier_main(Datum);
+
+typedef enum test_mode
+{
+       TEST_MODE_ALTERNATE,
+       TEST_MODE_REATTACH_RANDOM,
+       TEST_MODE_DETACH_RELEASES
+}                      test_mode;
+
+typedef struct test_barrier_alternate_state
+{
+       Barrier         barrier1;
+       Barrier         barrier2;
+       int                     loops;
+}                      test_barrier_alternate_state;
+
+typedef struct test_barrier_reattach_random_state
+{
+       Barrier         barrier;
+       int                     end_phase;
+}                      test_barrier_reattach_random_state;
+
+typedef struct test_barrier_detach_releases_state
+{
+       Barrier         barrier;
+}                      test_barrier_detach_releases_state;
+
+typedef struct test_barrier_state
+{
+       test_mode       mode;
+       union
+       {
+               test_barrier_alternate_state alternate_state;
+               test_barrier_reattach_random_state reattach_random_state;
+               test_barrier_detach_releases_state detach_releases_state;
+       };
+}                      test_barrier_state;
+
+/*
+ * Wait at barrier1 and then barrier2, state->loops times.
+ */
+static void
+do_test_barrier_alternate(test_barrier_alternate_state * state)
+{
+       int                     i;
+
+       for (i = 0; i < state->loops; ++i)
+       {
+               BarrierArriveAndWait(&state->barrier1, PG_WAIT_EXTENSION);
+               BarrierArriveAndWait(&state->barrier2, PG_WAIT_EXTENSION);
+       }
+}
+
+/*
+ * Attach, wait a random numer of times, then detach, repeatedly until
+ * state->end_phase is reached.
+ */
+static void
+do_test_barrier_reattach_random(test_barrier_reattach_random_state * state)
+{
+       bool            done = false;
+       int                     expected_phase;
+       int                     i;
+       int                     nwaits;
+
+       /* Make sure each backend uses a different pseudo-random sequence. */
+       srand48(getpid());
+
+       while (!done)
+       {
+               expected_phase = BarrierAttach(&state->barrier);
+               nwaits = (int) (lrand48() % 8);
+               for (i = 0; i < nwaits; ++i)
+               {
+                       if (expected_phase == state->end_phase)
+                       {
+                               done = true;
+                               break;
+                       }
+
+                       BarrierArriveAndWait(&state->barrier, 
PG_WAIT_EXTENSION);
+                       ++expected_phase;
+                       Assert(BarrierPhase(&state->barrier) == expected_phase);
+               }
+               BarrierDetach(&state->barrier);
+       }
+}
+
+/*
+ * Wait twice.  The first time gets us in sync with the leader
+ * process, and the second time we'll be released when the leader detaches.
+ * (In this case we're using a dynamic barrier, but the leader attached for
+ * the workers already which is a bit weird but necessary for this test.)
+ */
+static void
+do_test_barrier_detach_releases(test_barrier_detach_releases_state *state)
+{
+       /* Make sure the leader is here. */
+       BarrierArriveAndWait(&state->barrier, PG_WAIT_EXTENSION);
+       /* Wait for the leader to detach. */
+       BarrierArriveAndWait(&state->barrier, PG_WAIT_EXTENSION);
+}
+
+Datum
+test_barrier_main(Datum arg)
+{
+       dsm_segment *segment;
+       test_barrier_state *state;
+
+       BackgroundWorkerUnblockSignals();
+
+       CurrentResourceOwner = ResourceOwnerCreate(NULL, "test_barrier_main 
toplevel");
+
+       segment = dsm_attach(DatumGetInt32(arg));
+       state = (test_barrier_state *) dsm_segment_address(segment);
+       switch (state->mode)
+       {
+               case TEST_MODE_ALTERNATE:
+                       do_test_barrier_alternate(&state->alternate_state);
+                       break;
+               case TEST_MODE_REATTACH_RANDOM:
+                       
do_test_barrier_reattach_random(&state->reattach_random_state);
+                       break;
+               case TEST_MODE_DETACH_RELEASES:
+                       
do_test_barrier_detach_releases(&state->detach_releases_state);
+                       break;
+               default:
+                       Assert(0);
+       }
+       dsm_detach(segment);
+
+       return (Datum) 0;
+}
+
+static void
+launch_test(test_mode mode, int workers, int n)
+{
+       BackgroundWorkerHandle **handles;
+       test_barrier_state *state;
+       dsm_segment *segment;
+       int                     i;
+
+       handles = palloc(sizeof(BackgroundWorkerHandle *) * workers);
+
+       segment = dsm_create(sizeof(test_barrier_state), 0);
+       state = (test_barrier_state *) dsm_segment_address(segment);
+
+       /* Initialize state. */
+       state->mode = mode;
+       switch (mode)
+       {
+               case TEST_MODE_ALTERNATE:
+                       /* Initialize a static barrier for 'workers' workers. */
+                       state->alternate_state.loops = n;
+                       BarrierInit(&state->alternate_state.barrier1, workers);
+                       BarrierInit(&state->alternate_state.barrier2, workers);
+                       break;
+               case TEST_MODE_REATTACH_RANDOM:
+                       /* Initialize a dynamic barrier.  They'll attach and 
detach. */
+                       state->reattach_random_state.end_phase = n;
+                       BarrierInit(&state->reattach_random_state.barrier, 0);
+                       break;
+               case TEST_MODE_DETACH_RELEASES:
+                       /* Initialize a dynamic barrier. */
+                       BarrierInit(&state->detach_releases_state.barrier, 0);
+                       /* Attach on behalf of all participants... */
+                       for (i = 0; i < 1 + workers; ++i)
+                               
BarrierAttach(&state->detach_releases_state.barrier);
+                       break;
+               default:
+                       Assert(0);
+       }
+
+       /* Start workers. */
+       for (i = 0; i < workers; ++i)
+       {
+               BackgroundWorker bgw;
+
+               snprintf(bgw.bgw_name, sizeof(bgw.bgw_name), "worker%d", i);
+               bgw.bgw_flags = BGWORKER_SHMEM_ACCESS;
+               bgw.bgw_start_time = BgWorkerStart_ConsistentState;
+               bgw.bgw_restart_time = BGW_NEVER_RESTART;
+               snprintf(bgw.bgw_library_name, sizeof(bgw.bgw_library_name),
+                                "test_barrier");
+               snprintf(bgw.bgw_function_name, sizeof(bgw.bgw_function_name),
+                                "test_barrier_main");
+               bgw.bgw_main_arg = Int32GetDatum(dsm_segment_handle(segment));
+               bgw.bgw_notify_pid = MyProcPid;
+
+               if (!RegisterDynamicBackgroundWorker(&bgw, &handles[i]))
+                       elog(ERROR, "Can't start worker");
+       }
+
+       /* Some tests require the leader to do something. */
+       switch (mode)
+       {
+               case TEST_MODE_DETACH_RELEASES:
+                       /* Workers are waiting for us... */
+                       
BarrierArriveAndWait(&state->detach_releases_state.barrier,
+                                                                
PG_WAIT_EXTENSION);
+                       /*
+                        * Workers are still waiting for us, and we'll release 
them by
+                        * detaching.
+                        */
+                       BarrierDetach(&state->detach_releases_state.barrier);
+                       break;
+               default:
+                       ;
+       }
+
+       /* Wait for workers to complete. */
+       for (i = 0; i < workers; ++i)
+               WaitForBackgroundWorkerShutdown(handles[i]);
+
+       dsm_detach(segment);
+}
+
+Datum
+test_barrier_reattach_random(PG_FUNCTION_ARGS)
+{
+       int                     workers = PG_GETARG_INT32(0);
+       int                     end_phase = PG_GETARG_INT32(1);
+
+       launch_test(TEST_MODE_REATTACH_RANDOM, workers, end_phase);
+
+       return (Datum) 0;
+}
+
+Datum
+test_barrier_alternate(PG_FUNCTION_ARGS)
+{
+       int                     workers = PG_GETARG_INT32(0);
+       int                     loops = PG_GETARG_INT32(1);
+
+       launch_test(TEST_MODE_ALTERNATE, workers, loops);
+
+       return (Datum) 0;
+}
+
+Datum
+test_barrier_detach_releases(PG_FUNCTION_ARGS)
+{
+       int                     workers = PG_GETARG_INT32(0);
+
+       launch_test(TEST_MODE_DETACH_RELEASES, workers, 0);
+
+       return (Datum) 0;
+}
diff --git a/src/test/modules/test_barrier/test_barrier.control 
b/src/test/modules/test_barrier/test_barrier.control
new file mode 100644
index 00000000000..1cfe3bc8af3
--- /dev/null
+++ b/src/test/modules/test_barrier/test_barrier.control
@@ -0,0 +1,4 @@
+comment = 'test_barrier'
+default_version = '1.0'
+module_pathname = '$libdir/test_barrier'
+relocatable = true
-- 
2.15.0

Reply via email to