[v16,8/9] eal: implement functions for thread barrier management

Message ID 1633765318-28356-9-git-send-email-navasile@linux.microsoft.com (mailing list archive)
State Changes Requested, archived
Delegated to: David Marchand
Headers
Series eal: Add EAL API for threading |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Narcisa Ana Maria Vasile Oct. 9, 2021, 7:41 a.m. UTC
From: Narcisa Vasile <navasile@microsoft.com>

Add functions for barrier init, destroy, wait.

A portable type is used to represent a barrier identifier.
The rte_thread_barrier_wait() function returns the same value
on all platforms.

Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
---
 lib/eal/common/rte_thread.c  | 61 ++++++++++++++++++++++++++++++++++++
 lib/eal/include/rte_thread.h | 58 ++++++++++++++++++++++++++++++++++
 lib/eal/version.map          |  3 ++
 lib/eal/windows/rte_thread.c | 56 +++++++++++++++++++++++++++++++++
 4 files changed, 178 insertions(+)
  

Comments

Thomas Monjalon Oct. 12, 2021, 4:32 p.m. UTC | #1
09/10/2021 09:41, Narcisa Ana Maria Vasile:
> From: Narcisa Vasile <navasile@microsoft.com>
> 
> Add functions for barrier init, destroy, wait.
> 
> A portable type is used to represent a barrier identifier.
> The rte_thread_barrier_wait() function returns the same value
> on all platforms.
> 
> Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
> ---
>  lib/eal/common/rte_thread.c  | 61 ++++++++++++++++++++++++++++++++++++
>  lib/eal/include/rte_thread.h | 58 ++++++++++++++++++++++++++++++++++
>  lib/eal/version.map          |  3 ++
>  lib/eal/windows/rte_thread.c | 56 +++++++++++++++++++++++++++++++++
>  4 files changed, 178 insertions(+)

It doesn't need to be part of the API.
The pthread barrier is used only as part of the control thread implementation.
The need disappear if you implement control thread on Windows.
  
Narcisa Ana Maria Vasile Nov. 9, 2021, 2:07 a.m. UTC | #2
On Tue, Oct 12, 2021 at 06:32:09PM +0200, Thomas Monjalon wrote:
> 09/10/2021 09:41, Narcisa Ana Maria Vasile:
> > From: Narcisa Vasile <navasile@microsoft.com>
> > 
> > Add functions for barrier init, destroy, wait.
> > 
> > A portable type is used to represent a barrier identifier.
> > The rte_thread_barrier_wait() function returns the same value
> > on all platforms.
> > 
> > Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
> > ---
> >  lib/eal/common/rte_thread.c  | 61 ++++++++++++++++++++++++++++++++++++
> >  lib/eal/include/rte_thread.h | 58 ++++++++++++++++++++++++++++++++++
> >  lib/eal/version.map          |  3 ++
> >  lib/eal/windows/rte_thread.c | 56 +++++++++++++++++++++++++++++++++
> >  4 files changed, 178 insertions(+)
> 
> It doesn't need to be part of the API.
> The pthread barrier is used only as part of the control thread implementation.
> The need disappear if you implement control thread on Windows.
> 
Actually I think I have the implementation already. I've worked at this some time ago,
I have this patch:
[v4,2/6] eal: add function for control thread creation

The issue is I will break ABI so I cannot merge it as part of this patchset.
I'll see if I can remove this barrier patch though.
  
Narcisa Ana Maria Vasile Nov. 10, 2021, 3:13 a.m. UTC | #3
On Mon, Nov 08, 2021 at 06:07:34PM -0800, Narcisa Ana Maria Vasile wrote:
> On Tue, Oct 12, 2021 at 06:32:09PM +0200, Thomas Monjalon wrote:
> > 09/10/2021 09:41, Narcisa Ana Maria Vasile:
> > > From: Narcisa Vasile <navasile@microsoft.com>
> > > 
> > > Add functions for barrier init, destroy, wait.
> > > 
> > > A portable type is used to represent a barrier identifier.
> > > The rte_thread_barrier_wait() function returns the same value
> > > on all platforms.
> > > 
> > > Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
> > > ---
> > >  lib/eal/common/rte_thread.c  | 61 ++++++++++++++++++++++++++++++++++++
> > >  lib/eal/include/rte_thread.h | 58 ++++++++++++++++++++++++++++++++++
> > >  lib/eal/version.map          |  3 ++
> > >  lib/eal/windows/rte_thread.c | 56 +++++++++++++++++++++++++++++++++
> > >  4 files changed, 178 insertions(+)
> > 
> > It doesn't need to be part of the API.
> > The pthread barrier is used only as part of the control thread implementation.
> > The need disappear if you implement control thread on Windows.
> > 
> Actually I think I have the implementation already. I've worked at this some time ago,
> I have this patch:
> [v4,2/6] eal: add function for control thread creation
> 
> The issue is I will break ABI so I cannot merge it as part of this patchset.
> I'll see if I can remove this barrier patch though.

  I couldn't find a good way to test mutexes without barriers, so I kept this for now.
  

Patch

diff --git a/lib/eal/common/rte_thread.c b/lib/eal/common/rte_thread.c
index 476a8a1c08..580448d8bf 100644
--- a/lib/eal/common/rte_thread.c
+++ b/lib/eal/common/rte_thread.c
@@ -364,6 +364,67 @@  rte_thread_mutex_destroy(rte_thread_mutex *mutex)
 	return ret;
 }
 
+int
+rte_thread_barrier_init(rte_thread_barrier *barrier, int count)
+{
+	int ret = 0;
+	pthread_barrier_t *pthread_barrier = NULL;
+
+	RTE_VERIFY(barrier != NULL);
+	RTE_VERIFY(count > 0);
+
+	pthread_barrier = calloc(1, sizeof(*pthread_barrier));
+	if (pthread_barrier == NULL) {
+		RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n");
+		ret = ENOMEM;
+		goto cleanup;
+	}
+	ret = pthread_barrier_init(pthread_barrier, NULL, count);
+	if (ret != 0) {
+		RTE_LOG(DEBUG, EAL, "Failed to init barrier, ret = %d\n", ret);
+		goto cleanup;
+	}
+
+	barrier->barrier_id = pthread_barrier;
+	pthread_barrier = NULL;
+
+cleanup:
+	free(pthread_barrier);
+	return ret;
+}
+
+int
+rte_thread_barrier_wait(rte_thread_barrier *barrier)
+{
+	int ret = 0;
+
+	RTE_VERIFY(barrier != NULL);
+	RTE_VERIFY(barrier->barrier_id != NULL);
+
+	ret = pthread_barrier_wait(barrier->barrier_id);
+	if (ret == PTHREAD_BARRIER_SERIAL_THREAD)
+		ret = RTE_THREAD_BARRIER_SERIAL_THREAD;
+
+	return ret;
+}
+
+int
+rte_thread_barrier_destroy(rte_thread_barrier *barrier)
+{
+	int ret = 0;
+
+	RTE_VERIFY(barrier != NULL);
+
+	ret = pthread_barrier_destroy(barrier->barrier_id);
+	if (ret != 0)
+		RTE_LOG(DEBUG, EAL, "Failed to destroy barrier: %d\n", ret);
+
+	free(barrier->barrier_id);
+	barrier->barrier_id = NULL;
+
+	return ret;
+}
+
 int
 rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *))
 {
diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h
index b5782d396f..0adc854225 100644
--- a/lib/eal/include/rte_thread.h
+++ b/lib/eal/include/rte_thread.h
@@ -74,6 +74,18 @@  typedef struct rte_thread_mutex_tag {
 	void *mutex_id;  /**< mutex identifier */
 } rte_thread_mutex;
 
+/**
+ * Returned by rte_thread_barrier_wait() when call is successful.
+ */
+#define RTE_THREAD_BARRIER_SERIAL_THREAD -1
+
+/**
+ * Thread barrier representation.
+ */
+typedef struct rte_thread_barrier_tag {
+	void *barrier_id;  /**< barrrier identifier */
+} rte_thread_barrier;
+
 /**
  * TLS key type, an opaque pointer.
  */
@@ -410,6 +422,52 @@  int rte_thread_mutex_try_lock(rte_thread_mutex *mutex);
 __rte_experimental
 int rte_thread_mutex_destroy(rte_thread_mutex *mutex);
 
+/**
+ * Initializes a synchronization barrier.
+ *
+ * @param barrier
+ *    A pointer that references the newly created 'barrier' object.
+ *
+ * @param count
+ *    The number of threads that must enter the barrier before
+ *    the threads can continue execution.
+ *
+ * @return
+ *   On success, return 0.
+ *   On failure, return a positive errno-style error number.
+ */
+__rte_experimental
+int rte_thread_barrier_init(rte_thread_barrier *barrier, int count);
+
+/**
+ * Causes the calling thread to wait at the synchronization barrier 'barrier'.
+ *
+ * @param barrier
+ *    The barrier used for synchronizing the threads.
+ *
+ * @return
+ *   Return RTE_THREAD_BARRIER_SERIAL_THREAD for the thread synchronized
+ *      at the barrier.
+ *   Return 0 for all other threads.
+ *   Return a positive errno-style error number, in case of failure.
+ */
+__rte_experimental
+int rte_thread_barrier_wait(rte_thread_barrier *barrier);
+
+/**
+ * Releases all resources used by a synchronization barrier
+ * and uninitializes it.
+ *
+ * @param barrier
+ *    The barrier to be destroyed.
+ *
+ * @return
+ *   On success, return 0.
+ *   On failure, return a positive errno-style error number.
+ */
+__rte_experimental
+int rte_thread_barrier_destroy(rte_thread_barrier *barrier);
+
 /**
  * Create a TLS data key visible to all threads in the process.
  * the created key is later used to get/set a value.
diff --git a/lib/eal/version.map b/lib/eal/version.map
index 2506eb1587..ca05ba4f43 100644
--- a/lib/eal/version.map
+++ b/lib/eal/version.map
@@ -439,6 +439,9 @@  EXPERIMENTAL {
 	rte_thread_mutex_unlock;
 	rte_thread_mutex_try_lock;
 	rte_thread_mutex_destroy;
+	rte_thread_barrier_init;
+	rte_thread_barrier_wait;
+	rte_thread_barrier_destroy;
 };
 
 INTERNAL {
diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c
index 188e25e02e..11b4863fe8 100644
--- a/lib/eal/windows/rte_thread.c
+++ b/lib/eal/windows/rte_thread.c
@@ -568,6 +568,62 @@  rte_thread_mutex_destroy(rte_thread_mutex *mutex)
 	return 0;
 }
 
+int
+rte_thread_barrier_init(rte_thread_barrier *barrier, int count)
+{
+	int ret = 0;
+	SYNCHRONIZATION_BARRIER *sync_barrier = NULL;
+
+	RTE_VERIFY(barrier != NULL);
+	RTE_VERIFY(count > 0);
+
+	sync_barrier = calloc(1, sizeof(*sync_barrier));
+	if (sync_barrier == NULL) {
+		RTE_LOG(DEBUG, EAL, "Unable to initialize barrier. Insufficient memory!\n");
+		ret = ENOMEM;
+		goto cleanup;
+	}
+	if (!InitializeSynchronizationBarrier(sync_barrier, count, -1)) {
+		ret = thread_log_last_error("InitializeSynchronizationBarrier()");
+		goto cleanup;
+	}
+
+	barrier->barrier_id = sync_barrier;
+	sync_barrier = NULL;
+
+cleanup:
+	free(sync_barrier);
+	return ret;
+}
+
+int
+rte_thread_barrier_wait(rte_thread_barrier *barrier)
+{
+	RTE_VERIFY(barrier != NULL);
+	RTE_VERIFY(barrier->barrier_id != NULL);
+
+	if (EnterSynchronizationBarrier(barrier->barrier_id,
+				SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY)) {
+
+		return RTE_THREAD_BARRIER_SERIAL_THREAD;
+	}
+
+	return 0;
+}
+
+int
+rte_thread_barrier_destroy(rte_thread_barrier *barrier)
+{
+	RTE_VERIFY(barrier != NULL);
+
+	DeleteSynchronizationBarrier(barrier->barrier_id);
+
+	free(barrier->barrier_id);
+	barrier->barrier_id = NULL;
+
+	return 0;
+}
+
 int
 rte_thread_key_create(rte_thread_key *key,
 		__rte_unused void (*destructor)(void *))