[v16,8/9] eal: implement functions for thread barrier management
Checks
Commit Message
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
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.
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.
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.
@@ -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 *))
{
@@ -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.
@@ -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 {
@@ -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 *))