#include <pthread.h>
#include <pthread_np.h>
#include <unistd.h>
#include <util.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include "test.h"
static pthread_cond_t cond;
static pthread_mutex_t mutex;
static struct timespec expiretime;
static volatile int pv_state = 0;
static void
p(void)
{
CHECKr(pthread_mutex_lock(&mutex));
if (pv_state <= 0) {
CHECKr(pthread_cond_timedwait(&cond, &mutex, &expiretime));
}
pv_state--;
CHECKr(pthread_mutex_unlock(&mutex));
}
static void
v(void)
{
int needsignal;
CHECKr(pthread_mutex_lock(&mutex));
pv_state++;
needsignal = (pv_state == 1);
if (needsignal)
CHECKr(pthread_cond_signal(&cond));
CHECKr(pthread_mutex_unlock(&mutex));
}
static void
c1handler(void *arg)
{
CHECKe(close(*(int *)arg));
v();
}
static void *
child1fn(void *arg)
{
int fd, dummy;
char buf[1024];
int len;
SET_NAME("c1");
CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
CHECKe(openpty(&dummy, &fd, NULL, NULL, NULL));
pthread_cleanup_push(c1handler, (void *)&fd);
v();
while (1) {
CHECKe(len = read(fd, &buf, sizeof buf));
printf("child 1 read %d bytes\n", len);
}
pthread_cleanup_pop(0);
PANIC("child 1");
}
static int c2_in_test = 0;
static void
c2handler(void *arg)
{
ASSERT(c2_in_test);
v();
}
static int message_seen = 0;
static void *
child2fn(void *arg)
{
SET_NAME("c2");
CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL));
pthread_cleanup_push(c2handler, NULL);
v();
while (1) {
struct timespec now;
struct timespec end;
CHECKe(clock_gettime(CLOCK_REALTIME, &end));
end.tv_sec ++;
while (1) {
CHECKe(clock_gettime(CLOCK_REALTIME, &now));
if (timespeccmp(&now, &end, >=))
break;
pthread_yield();
}
c2_in_test = 1;
pthread_testcancel();
printf("you should see this message exactly once\n");
message_seen++;
c2_in_test = 0;
ASSERT(message_seen == 1);
v();
}
pthread_cleanup_pop(0);
PANIC("child 2");
}
static int c3_cancel_survived;
static void
c3handler(void *arg)
{
printf("(fyi, cancellation of self %s instantaneous)\n",
(c3_cancel_survived ? "was not" : "was"));
v();
}
static void *
child3fn(void *arg)
{
SET_NAME("c3");
pthread_cleanup_push(c3handler, NULL);
CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
c3_cancel_survived = 0;
pthread_cancel(pthread_self());
c3_cancel_survived = 1;
pthread_testcancel();
pthread_cleanup_pop(0);
PANIC("child 3");
}
static int c4_cancel_early;
static void
c4handler(void *arg)
{
printf("early = %d\n", c4_cancel_early);
ASSERT(c4_cancel_early == 0);
v();
}
static void *
child4fn(void *arg)
{
SET_NAME("c4");
pthread_cleanup_push(c4handler, NULL);
CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
c4_cancel_early = 3;
pthread_cancel(pthread_self());
c4_cancel_early = 2;
pthread_testcancel();
c4_cancel_early = 1;
CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
c4_cancel_early = 0;
pthread_testcancel();
pthread_cleanup_pop(0);
PANIC("child 4");
}
int
main(int argc, char *argv[])
{
pthread_t child1, child2, child3, child4;
CHECKr(pthread_mutex_init(&mutex, NULL));
CHECKr(pthread_cond_init(&cond, NULL));
CHECKe(clock_gettime(CLOCK_REALTIME, &expiretime));
expiretime.tv_sec += 5;
CHECKr(pthread_create(&child1, NULL, child1fn, NULL));
CHECKr(pthread_create(&child2, NULL, child2fn, NULL));
p();
p();
CHECKr(pthread_cancel(child1));
p();
p();
CHECKr(pthread_cancel(child2));
p();
CHECKr(pthread_create(&child3, NULL, child3fn, NULL));
p();
CHECKr(pthread_create(&child4, NULL, child4fn, NULL));
p();
CHECKr(pthread_join(child4, NULL));
CHECKr(pthread_join(child3, NULL));
CHECKr(pthread_join(child2, NULL));
CHECKr(pthread_join(child1, NULL));
exit(0);
}