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.
-/* 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--;
}
}
+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);
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);
-/* 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;
};
} 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);
-/* 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
// 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);
lock_thread_rw(thread);
thread->state |= ThreadJoinable;
unlock_thread_rw(thread);
+
+ knot_sem_post(&unit->_semaphore);
+
rcu_unregister_thread();
// Return
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;
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);
// 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];
pthread_mutex_unlock(&unit->_report_mx);
}
+ knot_sem_wait_post(&unit->_semaphore);
+
return KNOT_EOK;
}
-/* 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
#pragma once
#include <pthread.h>
+#include "contrib/semaphore.h"
#define DEFAULT_THR_COUNT 2 /*!< Default thread count. */
* 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;
/*!