Отказ от ответственности: Хотя моя проблема уже была решена @dunes, и я узнал, что разрешения обрабатываются на основе процесса (спасибо @solomon), я нашел в бешенстве оставить этот вопрос без ответа, который разрешаетсамо состояние гонки.
Как оказалось, я просто неправильно понял, как эти переменные cnd_t
должны использоваться.Хитрость заключается в том, чтобы заблокировать мьютекс до запуска рабочего потока и заставить его разблокировать мьютекс, когда он ожидает сигнала.При сигнале нити блокировка получается до , сигнализируя нить и не освобождается до тех пор, пока запрос не будет отправлен и сигнал не исчезнет.
Эта программа больше не имеет состояния гонки:
#include <assert.h>
#include <threads.h>
mtx_t mx_wakeup;
cnd_t cd_wakeup;
enum request {
REQ_NOTHING,
REQ_TERMINATE
} request;
int daemon(void *arg) {
(void)arg;
for(;;) {
request = REQ_NOTHING;
int retval = cnd_wait(&cd_wakeup, &mx_wakeup);
assert(retval == thrd_success);
if(request == REQ_TERMINATE) {
return 0;
}
}
}
void send(enum request request_) {
int retval;
// The worker thread will unlock the mutex implicitly when
// waiting for a signal, block until that happens.
retval = mtx_lock(&mx_wakeup);
assert(retval == thrd_success);
request = request_;
retval = cnd_signal(&cd_wakeup);
assert(retval == thrd_success);
// The worker thread needs to lock the mutex before waking up,
// this ensures that it doesn't before receiving the signal.
retval = mtx_unlock(&mx_wakeup);
assert(retval == thrd_success);
}
int main() {
int retval;
retval = mtx_init(&mx_wakeup, mtx_plain);
assert(retval == thrd_success);
retval = cnd_init(&cd_wakeup);
assert(retval == thrd_success);
// The mutex will be unlocked by the worker thread when listening.
retval = mtx_lock(&mx_wakeup);
assert(retval == thrd_success);
thrd_t thread;
retval = thrd_create(&thread, daemon, NULL);
assert(retval == thrd_success);
send(REQ_TERMINATE);
retval = thrd_join(thread, NULL);
assert(retval == thrd_success);
cnd_destroy(&cd_wakeup);
mtx_destroy(&mx_wakeup);
}
Ожидание рабочего потока просто добавляет другую переменную условия.Только код завершения должен быть принят для освобождения мьютекса, иначе главный поток будет ждать блокировки вечно.
#include <assert.h>
#include <threads.h>
mtx_t mx_wakeup;
cnd_t cd_wakeup, cd_complete;
enum request {
REQ_NOTHING,
REQ_TERMINATE
} request;
int daemon(void *arg) {
(void)arg;
int retval;
for(;;) {
request = REQ_NOTHING;
retval = cnd_wait(&cd_wakeup, &mx_wakeup);
assert(retval == thrd_success);
// The request can be processed here.
// Inform the main thread that the request was completed. The main
// thread can choose to wait or not.
retval = cnd_signal(&cd_complete);
assert(retval == thrd_success);
// Termination is different because the mutex wouldn't be released
// by the next `cnd_wait`, and must happend after the signal was send.
if(request == REQ_TERMINATE) {
retval = mtx_unlock(&mx_wakeup);
assert(retval == thrd_success);
return 0;
}
}
}
void send(enum request request_) {
int retval;
retval = mtx_lock(&mx_wakeup);
assert(retval == thrd_success);
request = request_;
retval = cnd_signal(&cd_wakeup);
assert(retval == thrd_success);
// This unlocks the mutex thus allowing the worker thread to process the
// request, thus the mutex can be reused here.
retval = cnd_wait(&cd_complete, &mx_wakeup);
assert(retval == thrd_success);
retval = mtx_unlock(&mx_wakeup);
assert(retval == thrd_success);
}
int main() {
int retval;
retval = mtx_init(&mx_wakeup, mtx_plain);
assert(retval == thrd_success);
retval = cnd_init(&cd_wakeup);
assert(retval == thrd_success);
// Remember to initialize the new conditional variable.
retval = cnd_init(&cd_complete);
assert(retval == thrd_success);
retval = mtx_lock(&mx_wakeup);
assert(retval == thrd_success);
thrd_t thread;
retval = thrd_create(&thread, daemon, NULL);
assert(retval == thrd_success);
send(REQ_TERMINATE);
retval = thrd_join(thread, NULL);
assert(retval == thrd_success);
cnd_destroy(&cd_wakeup);
mtx_destroy(&mx_wakeup);
}