From 612e7c2a873a11103801c24ce2c8361afa1fa195 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sat, 2 Jan 2021 15:05:06 +1300
Subject: [PATCH 2/3] Add pthread_barrier_t emulation.

Map pthread_barrier_t to native barriers on Windows.  Supply an
implementation based on other pthread primitives for macOS.

Discussion: https://postgr.es/m/20200227180100.zyvjwzcpiokfsqm2%40alap3.anarazel.de
---
 configure                       | 69 +++++++++++++++++++++++++++++++++
 configure.ac                    |  2 +
 src/include/pg_config.h.in      |  3 ++
 src/include/port/pg_pthread.h   | 40 +++++++++++++++++++
 src/port/pthread_barrier_wait.c | 64 ++++++++++++++++++++++++++++++
 5 files changed, 178 insertions(+)
 create mode 100644 src/include/port/pg_pthread.h
 create mode 100644 src/port/pthread_barrier_wait.c

diff --git a/configure b/configure
index 07529825d1..5a749b66da 100755
--- a/configure
+++ b/configure
@@ -11673,6 +11673,62 @@ if test "$ac_res" != no; then :
 
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_barrier_wait" >&5
+$as_echo_n "checking for library containing pthread_barrier_wait... " >&6; }
+if ${ac_cv_search_pthread_barrier_wait+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_barrier_wait ();
+int
+main ()
+{
+return pthread_barrier_wait ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_pthread_barrier_wait=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_pthread_barrier_wait+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_pthread_barrier_wait+:} false; then :
+
+else
+  ac_cv_search_pthread_barrier_wait=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_barrier_wait" >&5
+$as_echo "$ac_cv_search_pthread_barrier_wait" >&6; }
+ac_res=$ac_cv_search_pthread_barrier_wait
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
 # Solaris:
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing fdatasync" >&5
 $as_echo_n "checking for library containing fdatasync... " >&6; }
@@ -15845,6 +15901,19 @@ esac
 
 fi
 
+ac_fn_c_check_func "$LINENO" "pthread_barrier_wait" "ac_cv_func_pthread_barrier_wait"
+if test "x$ac_cv_func_pthread_barrier_wait" = xyes; then :
+  $as_echo "#define HAVE_PTHREAD_BARRIER_WAIT 1" >>confdefs.h
+
+else
+  case " $LIBOBJS " in
+  *" pthread_barrier_wait.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS pthread_barrier_wait.$ac_objext"
+ ;;
+esac
+
+fi
+
 ac_fn_c_check_func "$LINENO" "pwrite" "ac_cv_func_pwrite"
 if test "x$ac_cv_func_pwrite" = xyes; then :
   $as_echo "#define HAVE_PWRITE 1" >>confdefs.h
diff --git a/configure.ac b/configure.ac
index 7f855783f4..1d3cc1751c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1156,6 +1156,7 @@ AC_SEARCH_LIBS(getopt_long, [getopt gnugetopt])
 AC_SEARCH_LIBS(shm_open, rt)
 AC_SEARCH_LIBS(shm_unlink, rt)
 AC_SEARCH_LIBS(clock_gettime, [rt posix4])
+AC_SEARCH_LIBS(pthread_barrier_wait, pthread)
 # Solaris:
 AC_SEARCH_LIBS(fdatasync, [rt posix4])
 # Required for thread_test.c on Solaris
@@ -1734,6 +1735,7 @@ AC_REPLACE_FUNCS(m4_normalize([
 	link
 	mkdtemp
 	pread
+	pthread_barrier_wait
 	pwrite
 	random
 	srandom
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index ddaa9e8e18..b9f828385c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -421,6 +421,9 @@
 /* Define if you have POSIX threads libraries and header files. */
 #undef HAVE_PTHREAD
 
+/* Define to 1 if you have the `pthread_barrier_wait' function. */
+#undef HAVE_PTHREAD_BARRIER_WAIT
+
 /* Define to 1 if you have the `pthread_is_threaded_np' function. */
 #undef HAVE_PTHREAD_IS_THREADED_NP
 
diff --git a/src/include/port/pg_pthread.h b/src/include/port/pg_pthread.h
new file mode 100644
index 0000000000..21987429eb
--- /dev/null
+++ b/src/include/port/pg_pthread.h
@@ -0,0 +1,40 @@
+#ifndef PG_PTHREAD_H
+#define PG_PTHREAD_H
+
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+#ifndef HAVE_PTHREAD_BARRIER_WAIT
+
+#ifndef PTHREAD_BARRIER_SERIAL_THREAD
+#define PTHREAD_BARRIER_SERIAL_THREAD (-1)
+#endif
+
+#ifdef WIN32
+/* Forward to the appropriate Win32 primitives. */
+typedef SYNCHRONIZATION_BARRIER pthread_barrier_t;
+#else
+/*
+ * For systems that have no pthread barriers, but have the rest of the pthread
+ * stuff (that's at least macOS), we'll do our own simple barrier
+ * implementation.
+ */
+typedef struct pg_pthread_barrier
+{
+	bool		sense;			/* we only need a one bit phase */
+	int			party;			/* number of threads expected */
+	int			arrived;		/* number of threads that have arrived */
+	pthread_mutex_t mutex;
+	pthread_cond_t cv;
+} pthread_barrier_t;
+#endif
+
+extern int pthread_barrier_init(pthread_barrier_t *barrier, void *unused,
+								int party);
+extern int pthread_barrier_wait(pthread_barrier_t *barrier);
+extern int pthread_barrier_destroy(pthread_barrier_t *barrier);
+
+#endif
+
+#endif
diff --git a/src/port/pthread_barrier_wait.c b/src/port/pthread_barrier_wait.c
new file mode 100644
index 0000000000..6c8a0f29a9
--- /dev/null
+++ b/src/port/pthread_barrier_wait.c
@@ -0,0 +1,64 @@
+#include "postgres_fe.h"
+
+#include "port/pg_pthread.h"
+
+int
+pthread_barrier_init(pthread_barrier_t *barrier, void *unused, int party)
+{
+#ifdef WIN32
+	InitializeSynchronizationBarrier(barrier, party, 0);
+#else
+	barrier->sense = false;
+	barrier->party = party;
+	barrier->arrived = 0;
+	pthread_cond_init(&barrier->cv, NULL);
+	pthread_mutex_init(&barrier->mutex, NULL);
+	return 0;
+#endif
+}
+
+int
+pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+#ifdef WIN32
+	if (EnterSynchronizationBarrier(barrier,
+									SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY))
+		return PTHREAD_BARRIER_SERIAL_THREAD;
+#else
+	bool		initial_sense;
+
+	pthread_mutex_lock(&barrier->mutex);
+
+	/* We have arrived at the barrier. */
+	barrier->arrived++;
+	Assert(barrier->arrived <= barrier->party);
+
+	/* If we were the last to arrive, release the others and return. */
+	if (barrier->arrived == barrier->party)
+	{
+		barrier->arrived = 0;
+		barrier->sense = !barrier->sense;
+		pthread_mutex_unlock(&barrier->mutex);
+		pthread_cond_broadcast(&barrier->cv);
+
+		return PTHREAD_BARRIER_SERIAL_THREAD;
+	}
+
+	/* Wait for someone else to flip the sense. */
+	initial_sense = barrier->sense;
+	do
+	{
+		pthread_cond_wait(&barrier->cv, &barrier->mutex);
+	} while (barrier->sense == initial_sense);
+	pthread_mutex_unlock(&barrier->mutex);
+#endif
+
+	return 0;
+}
+
+int
+pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+	/* XXX DeleteSynchronizationBarrier() in Windows 8, Windows Server 2012? */
+	return 0;
+}
-- 
2.24.3 (Apple Git-128)

