133 lines
4.3 KiB
Plaintext
133 lines
4.3 KiB
Plaintext
|
From ac0507b3f45fe58100b528baeb8ca04270b4a8ff Mon Sep 17 00:00:00 2001
|
||
|
From: "Franklin \"Snaipe\" Mathieu" <me@snai.pe>
|
||
|
Date: Mon, 23 Mar 2020 05:52:23 +0000
|
||
|
Subject: timeout-posix: fix race condition
|
||
|
|
||
|
The posix timeout code was racy -- if a timeout was created, and
|
||
|
cancelled before the watchdog had any chance to run (because the worker
|
||
|
would exit too quickly, or because the thread would not be scheduled
|
||
|
quickly enough). This, in turn, made the watchdog wait forever for the
|
||
|
timeout queue to be nonempty.
|
||
|
|
||
|
This fixes the race by preventing the watchdog from ever waiting for the
|
||
|
queue to fill up -- it's actually not possible for the queue to be
|
||
|
empty during initialization, because the watchdog thread will be made to
|
||
|
wait for the initialization lock to be released. This means that the
|
||
|
only time where the queue is empty is when the watchdog has been
|
||
|
started, but the worker already exited/the timeout was cancelled.
|
||
|
|
||
|
In addition, this fix simplifies slightly the way that the watchdog is
|
||
|
collected -- we no longer try to join the thread, but we make it
|
||
|
detached from the get go.
|
||
|
|
||
|
This addresses Snaipe/Criterion#345.
|
||
|
|
||
|
diff --git a/src/timeout-posix.c b/src/timeout-posix.c
|
||
|
index 53bd181..2e9a210 100644
|
||
|
--- a/src/timeout-posix.c
|
||
|
+++ b/src/timeout-posix.c
|
||
|
@@ -22,13 +22,13 @@
|
||
|
* THE SOFTWARE.
|
||
|
*/
|
||
|
#include <assert.h>
|
||
|
+#include <errno.h>
|
||
|
#include <pthread.h>
|
||
|
-#include <time.h>
|
||
|
+#include <signal.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
-#include <errno.h>
|
||
|
-#include <signal.h>
|
||
|
#include <string.h>
|
||
|
+#include <time.h>
|
||
|
|
||
|
#include "config.h"
|
||
|
#include "sandbox.h"
|
||
|
@@ -48,11 +48,9 @@ static struct {
|
||
|
int thread_active;
|
||
|
pthread_mutex_t sync;
|
||
|
pthread_cond_t cond;
|
||
|
- pthread_cond_t termcond;
|
||
|
} self = {
|
||
|
.sync = PTHREAD_MUTEX_INITIALIZER,
|
||
|
.cond = PTHREAD_COND_INITIALIZER,
|
||
|
- .termcond = PTHREAD_COND_INITIALIZER,
|
||
|
};
|
||
|
|
||
|
static int timespec_cmp(struct timespec *a, struct timespec *b)
|
||
|
@@ -96,8 +94,6 @@ static void to_timespec(double timeout, struct timespec *timeo)
|
||
|
static void *timeout_killer_fn(void *nil)
|
||
|
{
|
||
|
pthread_mutex_lock(&self.sync);
|
||
|
- while (!self.requests)
|
||
|
- pthread_cond_wait(&self.cond, &self.sync);
|
||
|
|
||
|
struct bxfi_timeout_request *req;
|
||
|
for (;;) {
|
||
|
@@ -125,7 +121,7 @@ static void *timeout_killer_fn(void *nil)
|
||
|
free(req);
|
||
|
}
|
||
|
end:
|
||
|
- pthread_cond_broadcast(&self.termcond);
|
||
|
+ self.thread_active = 0;
|
||
|
pthread_mutex_unlock(&self.sync);
|
||
|
return nil;
|
||
|
}
|
||
|
@@ -137,10 +133,6 @@ void bxfi_reset_timeout_killer(void)
|
||
|
|
||
|
memcpy(&self.sync, &mutex, sizeof (mutex));
|
||
|
memcpy(&self.cond, &cond, sizeof (cond));
|
||
|
- memcpy(&self.termcond, &cond, sizeof (cond));
|
||
|
-
|
||
|
- if (self.requests)
|
||
|
- pthread_join(self.thread, NULL);
|
||
|
}
|
||
|
|
||
|
int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||
|
@@ -159,10 +151,16 @@ int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||
|
|
||
|
pthread_mutex_lock(&self.sync);
|
||
|
if (!self.requests) {
|
||
|
- if (self.thread_active)
|
||
|
- pthread_join(self.thread, NULL);
|
||
|
+ pthread_attr_t attrs;
|
||
|
+ if ((rc = pthread_attr_init(&attrs)) == -1) {
|
||
|
+ rc = -errno;
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+ pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
||
|
+
|
||
|
self.thread_active = 1;
|
||
|
- rc = -pthread_create(&self.thread, NULL, timeout_killer_fn, NULL);
|
||
|
+ rc = -pthread_create(&self.thread, &attrs, timeout_killer_fn, NULL);
|
||
|
+ pthread_attr_destroy(&attrs);
|
||
|
if (rc)
|
||
|
goto error;
|
||
|
}
|
||
|
@@ -177,7 +175,6 @@ int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||
|
*nptr = req;
|
||
|
|
||
|
pthread_cond_broadcast(&self.cond);
|
||
|
- pthread_cond_broadcast(&self.termcond);
|
||
|
pthread_mutex_unlock(&self.sync);
|
||
|
return 0;
|
||
|
|
||
|
@@ -204,17 +201,6 @@ void bxfi_cancel_timeout(struct bxfi_sandbox *instance)
|
||
|
}
|
||
|
if (cancelled) {
|
||
|
pthread_cond_broadcast(&self.cond);
|
||
|
- if (!self.requests) {
|
||
|
- while (self.cancelled && !self.requests)
|
||
|
- pthread_cond_wait(&self.termcond, &self.sync);
|
||
|
- if (self.requests)
|
||
|
- goto end;
|
||
|
- if (self.thread_active) {
|
||
|
- pthread_join(self.thread, NULL);
|
||
|
- self.thread_active = 0;
|
||
|
- }
|
||
|
- }
|
||
|
}
|
||
|
-end:
|
||
|
pthread_mutex_unlock(&self.sync);
|
||
|
}
|