]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
dthreads: patch early join of destructable threads
authorJan Hák <jan.hak@nic.cz>
Mon, 5 Sep 2022 13:41:59 +0000 (15:41 +0200)
committerLibor Peltan <libor.peltan@nic.cz>
Wed, 21 Sep 2022 07:52:32 +0000 (09:52 +0200)
There is a flaw in the original implementation that causes `dt_join` to exit before the destructor runs. This patch does not fix this flaw, but covers it.

src/contrib/semaphore.c
src/contrib/semaphore.h
src/knot/server/dthreads.c
src/knot/server/dthreads.h

index ad50dcce6e563c5304d7eaef093d98628e8e9fab..6dc051936e5a88dd0ae0e9bd6f8da15d02dfe6ee 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "semaphore.h"
 
 #include <assert.h>
+#include <limits.h>
 #include <stdlib.h>
 
 #if defined(__APPLE__)
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif
 
-void knot_sem_init(knot_sem_t *sem, unsigned int value)
+#define SEM_STATUS_POSIX INT_MIN
+
+void knot_sem_init(knot_sem_t *sem, int value)
 {
+       assert((sem != NULL) && (value != SEM_STATUS_POSIX));
+       if (value < 0) {
+               goto nonposix;
+       }
        int ret = sem_init(&sem->semaphore, 1, value);
        if (ret == 0) {
-               sem->status = -1;
-       } else {
-               sem->status = value;
-               sem->status_lock = malloc(sizeof(*sem->status_lock));
-               pthread_mutex_init(&sem->status_lock->mutex, NULL);
-               pthread_cond_init(&sem->status_lock->cond, NULL);
+               sem->status = SEM_STATUS_POSIX;
+               return;
        }
+nonposix:
+       knot_sem_init_nonposix(sem, value);
+}
+
+void knot_sem_init_nonposix(knot_sem_t *sem, int value)
+{
+       assert((sem != NULL) && (value != SEM_STATUS_POSIX));
+       sem->status = value;
+       sem->status_lock = malloc(sizeof(*sem->status_lock));
+       pthread_mutex_init(&sem->status_lock->mutex, NULL);
+       pthread_cond_init(&sem->status_lock->cond, NULL);
+}
+
+void knot_sem_reset(knot_sem_t *sem, int value)
+{
+       assert((sem != NULL) && (value != SEM_STATUS_POSIX) && (sem->status != SEM_STATUS_POSIX));
+       pthread_mutex_lock(&sem->status_lock->mutex);
+       sem->status = value;
+       pthread_cond_signal(&sem->status_lock->cond);
+       pthread_mutex_unlock(&sem->status_lock->mutex);
 }
 
+
 void knot_sem_wait(knot_sem_t *sem)
 {
-       if (sem->status < 0) {
+       assert(sem != NULL);
+       if (sem->status == SEM_STATUS_POSIX) {
                int semret;
                do {
                        semret = sem_wait(&sem->semaphore);
                } while (semret != 0); // repeat wait as it might be interrupted by a signal
        } else {
                pthread_mutex_lock(&sem->status_lock->mutex);
-               while (sem->status == 0) {
+               while (sem->status <= 0) {
                        pthread_cond_wait(&sem->status_lock->cond, &sem->status_lock->mutex);
                }
                sem->status--;
@@ -53,9 +78,29 @@ void knot_sem_wait(knot_sem_t *sem)
        }
 }
 
+void knot_sem_wait_post(knot_sem_t *sem)
+{
+       assert((sem != NULL) && (sem->status != SEM_STATUS_POSIX));
+       pthread_mutex_lock(&sem->status_lock->mutex);
+       while (sem->status <= 0) {
+               pthread_cond_wait(&sem->status_lock->cond, &sem->status_lock->mutex);
+       }
+       pthread_cond_signal(&sem->status_lock->cond);
+       pthread_mutex_unlock(&sem->status_lock->mutex);
+}
+
+void knot_sem_get_ahead(knot_sem_t *sem)
+{
+       assert((sem != NULL) && (sem->status != SEM_STATUS_POSIX));
+       pthread_mutex_lock(&sem->status_lock->mutex);
+       sem->status--;
+       pthread_mutex_unlock(&sem->status_lock->mutex);
+}
+
 void knot_sem_post(knot_sem_t *sem)
 {
-       if (sem->status < 0) {
+       assert(sem != NULL);
+       if (sem->status == SEM_STATUS_POSIX) {
                int semret = sem_post(&sem->semaphore);
                (void)semret;
                assert(semret == 0);
@@ -69,8 +114,9 @@ void knot_sem_post(knot_sem_t *sem)
 
 void knot_sem_destroy(knot_sem_t *sem)
 {
+       assert(sem != NULL);
        knot_sem_wait(sem);
-       if (sem->status < 0) {
+       if (sem->status == SEM_STATUS_POSIX) {
                sem_destroy(&sem->semaphore);
        } else {
                pthread_cond_destroy(&sem->status_lock->cond);
index be49b7fe260ec5dbcbe094074e75c04067b17265..33acb8c5fbfb9a8646a22749dd30f75188668e24 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+#pragma once
+
 #include <pthread.h>
 #include <semaphore.h>
 
-#pragma once
-
 typedef struct {
        pthread_mutex_t mutex;
        pthread_cond_t cond;
@@ -32,10 +32,18 @@ typedef struct {
        };
 } knot_sem_t;
 
-void knot_sem_init(knot_sem_t *sem, unsigned int value);
+void knot_sem_init(knot_sem_t *sem, int value);
+
+void knot_sem_init_nonposix(knot_sem_t *sem, int value);
+
+void knot_sem_reset(knot_sem_t *sem, int value);
 
 void knot_sem_wait(knot_sem_t *sem);
 
+void knot_sem_wait_post(knot_sem_t *sem);
+
+void knot_sem_get_ahead(knot_sem_t *sem);
+
 void knot_sem_post(knot_sem_t *sem);
 
 void knot_sem_destroy(knot_sem_t *sem);
index 74203ac86efbe8e86eb13d6fac17991580fb5c7b..66991e46a99eb976322f2738f74c5760fb2607af 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -172,8 +172,13 @@ static void *thread_ep(void *data)
                        // Signalize state change
                        unit_signalize_change(unit);
 
+                       knot_sem_post(&unit->_semaphore);
+
                        // Wait for notification from unit
                        pthread_cond_wait(&unit->_notify, &unit->_notify_mx);
+
+                       knot_sem_get_ahead(&unit->_semaphore);
+
                        pthread_mutex_unlock(&unit->_notify_mx);
                } else {
                        unlock_thread_rw(thread);
@@ -191,6 +196,9 @@ static void *thread_ep(void *data)
        lock_thread_rw(thread);
        thread->state |= ThreadJoinable;
        unlock_thread_rw(thread);
+
+       knot_sem_post(&unit->_semaphore);
+
        rcu_unregister_thread();
 
        // Return
@@ -294,6 +302,9 @@ static dt_unit_t *dt_create_unit(int count)
                return 0;
        }
 
+       // Init value to `1` ensures that not started dt_unit_t will block on destruction
+       knot_sem_init_nonposix(&unit->_semaphore, 1);
+
        // Save unit size
        unit->size = count;
 
@@ -405,6 +416,9 @@ void dt_delete(dt_unit_t **unit)
        pthread_cond_destroy(&d_unit->_notify);
        pthread_cond_destroy(&d_unit->_report);
 
+       // Deinit semaphore
+       knot_sem_destroy(&d_unit->_semaphore);
+
        // Free memory
        free(d_unit->threads);
        free(d_unit);
@@ -458,6 +472,9 @@ int dt_start(dt_unit_t *unit)
        // Lock unit
        pthread_mutex_lock(&unit->_notify_mx);
        dt_unit_lock(unit);
+
+       knot_sem_reset(&unit->_semaphore, -unit->size + 1);
+
        for (int i = 0; i < unit->size; ++i) {
 
                dthread_t *thread = unit->threads[i];
@@ -546,6 +563,8 @@ int dt_join(dt_unit_t *unit)
                pthread_mutex_unlock(&unit->_report_mx);
        }
 
+       knot_sem_wait_post(&unit->_semaphore);
+
        return KNOT_EOK;
 }
 
index 0c243a1c325ebb8d60230064ada8de2727a5bc58..ec8ef47c0a91663524215b78de6e6bd8b7c4a125 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
 #pragma once
 
 #include <pthread.h>
+#include "contrib/semaphore.h"
 
 #define DEFAULT_THR_COUNT 2  /*!< Default thread count. */
 
@@ -89,13 +90,14 @@ typedef struct dthread {
  * the same runnable.
  */
 typedef struct dt_unit {
-       int                   size; /*!< Unit width (number of threads) */
-       struct dthread   **threads; /*!< Array of threads */
-       pthread_cond_t     _notify; /*!< Notify thread */
-       pthread_mutex_t _notify_mx; /*!< Condition mutex */
-       pthread_cond_t     _report; /*!< Report thread state */
-       pthread_mutex_t _report_mx; /*!< Condition mutex */
-       pthread_mutex_t        _mx; /*!< Unit lock */
+       int                    size; /*!< Unit width (number of threads) */
+       struct dthread    **threads; /*!< Array of threads */
+       pthread_cond_t      _notify; /*!< Notify thread */
+       pthread_mutex_t  _notify_mx; /*!< Condition mutex */
+       pthread_cond_t      _report; /*!< Report thread state */
+       pthread_mutex_t  _report_mx; /*!< Condition mutex */
+       pthread_mutex_t         _mx; /*!< Unit lock */
+       knot_sem_t       _semaphore; /*!< Join semaphore */
 } dt_unit_t;
 
 /*!