Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions tests/src/thpool_destroy_hang.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Reproduces the “hang inside thpool_destroy()” bug.
* – Fails (SIGALRM) with the original binary-semaphore implementation.
* – Succeeds instantly once bsem_wait / bsem_post_all are patched.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include "../../thpool.h"

#define TIMEOUT 1

/* -------------------------------------------------------------- */
/* 2. SIGALRM handler: triggers when thpool_destroy() does not */
/* return within 1 sec, indicating the bug is reproduced. */
/* -------------------------------------------------------------- */
static void timeout_handler(int sig)
{
(void)sig;
fprintf(stderr,
"FAIL: thpool_destroy() did not finish within %d s "
"(bug reproduced)\n", TIMEOUT);
_exit(EXIT_FAILURE);
}

int main(void)
{
const int THREADS = 4000;

/* Watchdog: if we are still alive after 3 s, the bug is present */
signal(SIGALRM, timeout_handler);
alarm(TIMEOUT);

printf("Creating pool with %d threads …\n", THREADS);
threadpool tp = thpool_init(THREADS);

struct timeval t0, t1;
gettimeofday(&t0, NULL);

printf("Calling thpool_destroy() …\n");
thpool_destroy(tp); /* ← hangs here when not patched */

gettimeofday(&t1, NULL);
alarm(0); /* disarm watchdog */

double elapsed =
(t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6;

printf("PASS: thpool_destroy() returned in %.3f s (patch OK)\n",
elapsed);
return 0;
}
25 changes: 25 additions & 0 deletions tests/thpool_destroy_hang.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#! /bin/bash
#
# Test for the deterministic thpool_destroy() hang
#

. funcs.sh

# -------------------------------------------------- #
# Single test: must finish without hitting SIGALRM #
# -------------------------------------------------- #
function test_thpool_destroy_hang {
compile src/thpool_destroy_hang.c
echo "Testing for thpool_destroy() hang"
output=$(timeout 2 ./test)
if [[ $? != 0 ]]; then
err "thpool_destroy() hang reproduced" "$output"
exit 1
fi

}

# Run the test
test_thpool_destroy_hang

echo "No errors"
7 changes: 4 additions & 3 deletions thpool.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#if defined(__linux__)
#include <sys/prctl.h>
#endif
Expand Down Expand Up @@ -554,7 +555,7 @@ static void bsem_post(bsem *bsem_p) {
/* Post to all threads */
static void bsem_post_all(bsem *bsem_p) {
pthread_mutex_lock(&bsem_p->mutex);
bsem_p->v = 1;
bsem_p->v = INT_MAX;
pthread_cond_broadcast(&bsem_p->cond);
pthread_mutex_unlock(&bsem_p->mutex);
}
Expand All @@ -563,9 +564,9 @@ static void bsem_post_all(bsem *bsem_p) {
/* Wait on semaphore until semaphore has value 0 */
static void bsem_wait(bsem* bsem_p) {
pthread_mutex_lock(&bsem_p->mutex);
while (bsem_p->v != 1) {
while (bsem_p->v == 0) {
pthread_cond_wait(&bsem_p->cond, &bsem_p->mutex);
}
bsem_p->v = 0;
bsem_p->v--;
pthread_mutex_unlock(&bsem_p->mutex);
}