From 4ba3fe2e318dd1a7b54b3b9ae8853531ed3a6e12 Mon Sep 17 00:00:00 2001 From: Thomas Munro 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 +#include + +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