On Wed, Sep 2, 2015 at 2:02 AM, Robert Haas <robertmh...@gmail.com> wrote:

> On Mon, Aug 31, 2015 at 8:01 AM, Ashutosh Bapat
> <ashutosh.ba...@enterprisedb.com> wrote:
> >> Thanks.  It needs testing though to see if it really works as
> >> intended.  Can you look into that?
> >
> > PFA the patch containing your code changes + test module. See if that
> meets
> > your expectations.
>
>
PFA patch with improved test module and fix for a bug.

bgworker_sigusr1_handler() should set the latch when set_latch_on_sigusr1
is true, similar to procsignal_sigusr1_handler(). Without this fix, if a
background worker without DATABASE_CONNECTION flag calls
WaitForBackgroundWorker*() functions, those functions wait indefinitely as
the latch is never set upon receipt of SIGUSR1.


> Thanks.  I don't think this test module is suitable for commit for a
> number of reasons, including the somewhat hackish use of exit(0)
> instead of proper error reporting


I have changed this part of code.


> , the fact that you didn't integrate
> it into the Makefile structure properly


What was missing? I am able to make {check,clean,install) from the
directory. I can also make -C <dirpath> check from repository's root.


> and the fact that without the
> postmaster.c changes it hangs forever instead of causing a test
> failure.


Changed this too. The SQL level function test_bgwnotify() now errors out if
it doesn't receive notification in specific time.


> But it's sufficient to show that the code changes have the
> intended effect.
>

Looking at the kind of bugs I am getting, we should commit this test
module. Let me know your comments, I will fix those.


> I've committed this and back-patched it to 9.5, but not further.  It's
> a bug fix, but it's also a refactoring exercise, so I'd rather not
> push it into released branches.
>
> --
> Robert Haas
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>



-- 
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 68c9505..fbbf97e 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -556,20 +556,23 @@ bgworker_die(SIGNAL_ARGS)
  * Standard SIGUSR1 handler for unconnected workers
  *
  * Here, we want to make sure an unconnected worker will at least heed
  * latch activity.
  */
 static void
 bgworker_sigusr1_handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	if (set_latch_on_sigusr1)
+		SetLatch(MyLatch);
+
 	latch_sigusr1_handler();
 
 	errno = save_errno;
 }
 
 /*
  * Start a new background worker
  *
  * This is the main entry point for background worker, to be called from
  * postmaster.
diff --git a/src/test/modules/test_bgwnotify/Makefile b/src/test/modules/test_bgwnotify/Makefile
new file mode 100644
index 0000000..6931c09
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_bgwnotify/Makefile
+
+MODULE_big = test_bgwnotify
+OBJS = test_bgwnotify.o $(WIN32RES)
+PGFILEDESC = "test_bgwnotify - example use of background worker notification infrastructure"
+
+EXTENSION = test_bgwnotify
+DATA = test_bgwnotify--1.0.sql
+
+REGRESS = test_bgwnotify
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_bgwnotify
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_bgwnotify/expected/test_bgwnotify.out b/src/test/modules/test_bgwnotify/expected/test_bgwnotify.out
new file mode 100644
index 0000000..b7e1b65
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/expected/test_bgwnotify.out
@@ -0,0 +1,11 @@
+CREATE EXTENSION test_bgwnotify;
+--
+-- We're checking that the operations complete without crashing or hanging and
+-- that none of their internal sanity tests fail.
+--
+SELECT test_bgwnotify();
+ test_bgwnotify 
+----------------
+ t
+(1 row)
+
diff --git a/src/test/modules/test_bgwnotify/sql/test_bgwnotify.sql b/src/test/modules/test_bgwnotify/sql/test_bgwnotify.sql
new file mode 100644
index 0000000..e448ef0
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/sql/test_bgwnotify.sql
@@ -0,0 +1,7 @@
+CREATE EXTENSION test_bgwnotify;
+
+--
+-- We're checking that the operations complete without crashing or hanging and
+-- that none of their internal sanity tests fail.
+--
+SELECT test_bgwnotify();
diff --git a/src/test/modules/test_bgwnotify/test_bgwnotify--1.0.sql b/src/test/modules/test_bgwnotify/test_bgwnotify--1.0.sql
new file mode 100644
index 0000000..f3423bc
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/test_bgwnotify--1.0.sql
@@ -0,0 +1,7 @@
+/* src/test/modules/test_bgwnotify/test_bgwnotify--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_bgwnotify" to load this file. \quit
+
+CREATE FUNCTION test_bgwnotify() RETURNS pg_catalog.bool STRICT
+	AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_bgwnotify/test_bgwnotify.c b/src/test/modules/test_bgwnotify/test_bgwnotify.c
new file mode 100644
index 0000000..8966b0e
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/test_bgwnotify.c
@@ -0,0 +1,239 @@
+/* -------------------------------------------------------------------------
+ *
+ * test_bgwnotify.c
+ * Test for background worker notify feature. The SQL script for the test calls
+ * test_bgwnotify() function. This function in turn runs launcher background
+ * workers in different configurations. The launcher in turn runs one 
+ * background workers, which does nothing but exit after sleeping for a while.
+ * The launcher waits for the workers to finish. The function test_bgwnotify()
+ * waits for launcher to quit. If launcher doesn't quit within definite time,
+ * test_bgwnotify() throws error and kills the launcher.
+ *
+ * Copyright (C) 2015, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/test_bgwnotify/test_bgwnotify.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+/* These are always necessary for a bgworker */
+#include "miscadmin.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/lwlock.h"
+#include "storage/proc.h"
+#include "storage/shmem.h"
+
+/* these headers are used by this particular worker's code */
+#include "fmgr.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_bgwnotify);
+void test_bgwnotify_launcher_main(Datum datum);
+void test_bgwnotify_worker_main(Datum datum);
+
+/* flags set by signal handlers */
+static volatile sig_atomic_t got_sigusr1 = false;
+
+/* Worker sleep time in seconds */
+static int	worker_sleeptime = 5;
+
+/*
+ * Signal handler for SIGUSR1
+ *		Set a flag to tell the main loop to check status of worker processes,
+ *		and set our latch to wake it up.
+ */
+static void
+test_bgwnotify_sigusr1(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	got_sigusr1 = true;
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+static void
+wait_for_launcher_shutdown(BackgroundWorkerHandle *handle)
+{
+	int 					pid;
+	BgwHandleStatus			status;
+	BgwHandleStatus			expected_status;
+
+	expected_status = BGWH_STARTED;
+	while (true)
+	{
+		int			rc;
+
+		CHECK_FOR_INTERRUPTS();
+
+		/* Be on the safe side and wait for twice as much time as a worker */
+		rc = WaitLatch(MyLatch,
+				   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+				   worker_sleeptime * 2 * 1000L);
+		ResetLatch(MyLatch);
+
+		if (got_sigusr1)
+		{
+			got_sigusr1 = false;
+			status = GetBackgroundWorkerPid(handle, &pid);
+			if (status != expected_status)
+				elog(ERROR, "found launcher in unexpected state (expected status %d, got %d), exiting.",
+							expected_status, status);
+
+			if (status == BGWH_STARTED)
+			{
+				expected_status = BGWH_STOPPED;
+				continue;
+			}
+			else if (status == BGWH_STOPPED)
+			{
+				/* wait is over, return */
+				return;
+			}
+		}
+
+		if (rc & WL_TIMEOUT)
+		{
+			TerminateBackgroundWorker(handle);
+			elog(ERROR, "did not receive notification from the background worker within expected time"); 
+		}
+	}
+}
+
+/*
+ * This function runs the launcher background worker in different
+ * configurations. The function raises an error if the launcher background
+ * worker doesn't exit within stipulated time.
+ */
+void
+test_bgwnotify_launcher_main(Datum main_arg)
+{
+	BackgroundWorker 		worker;
+	BackgroundWorkerHandle	*handle;
+	int						pid;
+
+	/* We're now ready to receive signals */
+	BackgroundWorkerUnblockSignals();
+
+	/* Register one background worker */
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_restart_time = BGW_NEVER_RESTART;
+	worker.bgw_main = NULL;		/* new worker might not have library loaded */
+	sprintf(worker.bgw_library_name, "test_bgwnotify");
+	sprintf(worker.bgw_function_name, "test_bgwnotify_worker_main");
+	worker.bgw_notify_pid = MyProcPid;
+	snprintf(worker.bgw_name, BGW_MAXLEN, "test_bgwnotify_worker");
+	worker.bgw_main_arg = 0;
+
+	/* Launch the worker and wait for it to quit */
+	RegisterDynamicBackgroundWorker(&worker, &handle);
+	
+	/*
+	 * First wait for the background worker to start and then for it to quit.
+	 * In case, the notifications are not received in time, the launcher thread
+	 * will wait indefinitely. The backend which started the launcher, however
+	 * waits for definite time and errors out when it doesn't receive the
+	 * notification in time. At that time, it will also terminate us.
+	 */
+	WaitForBackgroundWorkerStartup(handle, &pid);
+	
+	WaitForBackgroundWorkerShutdown(handle);
+
+	proc_exit(0);
+}
+
+void
+test_bgwnotify_worker_main(Datum main_arg)
+{
+	int			rc;
+
+	/* We're now ready to receive signals */
+	BackgroundWorkerUnblockSignals();
+
+	ResetLatch(MyLatch);
+	/*
+	 * The worker registered will wait for worker_sleeptime and is expected to
+	 * quit then. The launcher is expected to get a notification when the worker
+	 * quits. Let the launcher wait for twice the time the worker waits. Raise
+	 * error if the launcher doesn't receive the noitification. 
+	 */
+	rc = WaitLatch(MyLatch,
+				   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+				   worker_sleeptime * 1000L);
+
+	/*
+	 * Only timeout should wake up this worker. Everything else is error
+	 * condition.
+	 */
+	if (rc & WL_TIMEOUT)
+		proc_exit(0);
+
+	/* Anything else is unexpected. Throw error and exit. */
+	elog(WARNING, "unexpected event (rc = %d) occured, exiting", rc);
+	proc_exit(1);
+}
+
+/*
+ * Dynamically launch an SPI worker.
+ */
+Datum
+test_bgwnotify(PG_FUNCTION_ARGS)
+{
+	BackgroundWorker worker;
+	BackgroundWorkerHandle *handle;
+
+	/* Establish signal handlers before unblocking signals. */
+	pqsignal(SIGUSR1, test_bgwnotify_sigusr1);
+
+	/* test1: shared memory and database access */
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
+						BGWORKER_BACKEND_DATABASE_CONNECTION;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_restart_time = BGW_NEVER_RESTART;
+	worker.bgw_main = NULL;		/* new worker might not have library loaded */
+	sprintf(worker.bgw_library_name, "test_bgwnotify");
+	sprintf(worker.bgw_function_name, "test_bgwnotify_launcher_main");
+	snprintf(worker.bgw_name, BGW_MAXLEN, "test_bgwnotify_launcher");
+	worker.bgw_main_arg = 0; 
+	worker.bgw_notify_pid = MyProcPid;
+	got_sigusr1 = false;
+
+	if (!RegisterDynamicBackgroundWorker(&worker, &handle))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * Wait for the launcher to quit. The function makes sure that the launcher
+	 * has been terminated before quitting from the function.
+	 */
+	wait_for_launcher_shutdown(handle);
+
+	/* test2: shared memory access */
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_restart_time = BGW_NEVER_RESTART;
+	worker.bgw_main = NULL;		/* new worker might not have library loaded */
+	sprintf(worker.bgw_library_name, "test_bgwnotify");
+	sprintf(worker.bgw_function_name, "test_bgwnotify_launcher_main");
+	snprintf(worker.bgw_name, BGW_MAXLEN, "test_bgwnotify_launcher");
+	worker.bgw_main_arg = 0; 
+	worker.bgw_notify_pid = MyProcPid;
+	got_sigusr1 = false;
+
+	if (!RegisterDynamicBackgroundWorker(&worker, &handle))
+		PG_RETURN_BOOL(false);
+
+	/*
+	 * Wait for the launcher to quit. The function makes sure that the launcher
+	 * has been terminated before quitting from the function.
+	 */
+	wait_for_launcher_shutdown(handle);
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/test/modules/test_bgwnotify/test_bgwnotify.control b/src/test/modules/test_bgwnotify/test_bgwnotify.control
new file mode 100644
index 0000000..c2881fc
--- /dev/null
+++ b/src/test/modules/test_bgwnotify/test_bgwnotify.control
@@ -0,0 +1,4 @@
+comment = 'Test code for background worker notification'
+default_version = '1.0'
+module_pathname = '$libdir/test_bgwnotify'
+relocatable = true
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to