Skip to content

Commit fa679d8

Browse files
committed
exhaustive-libptimc seems to be the best
1 parent 04b3f10 commit fa679d8

File tree

13 files changed

+830
-0
lines changed

13 files changed

+830
-0
lines changed

Diff for: exhaust-libptimc/README

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
##### Exhaustive-parallel implementation
2+
3+
Basically:
4+
5+
- The master sets itself up as a subreaper
6+
7+
- @N control pipes are opened
8+
9+
- The master forks off an initial worker, with id 0
10+
11+
- On every iteration, the master reads from each control pipe. Processes
12+
must ask for permission before dying; the master will give them
13+
permission after recording the death.
14+
15+
- After allowing process deaths, if there are empty slots, it picks a
16+
random living child. It sends that child a control message telling it to
17+
fork, and which id to use.
18+
19+
- The child *must* respond to this control message, with either success or
20+
failure.
21+
22+
#####
23+
24+
# libpthreadsimc
25+
26+
Wrapper for the POSIX Threads API. It should have identical behavior, except
27+
that it tries running the code _with every possible interleaving_.
28+
29+
Basically, need to do green threading. Keep a list of all threads. At each
30+
iteration, pick one of the threads to run for some period of time. Fork on the
31+
choice of which thread to run.
32+
33+
Currently, we require the user to insert yield() calls, and we only switch
34+
threads at those yield calls.
35+
36+
Eventually, we would like to support automatic single-stepping so these
37+
explicit yield annotations aren't needed. That should be relatively easy to do;
38+
fork off a manager thread that single-steps the underlying process when
39+
requested. But for now we can assume we have yields.
40+
41+
#### Architecture
42+
43+
libimc handles basic branching/choice; it knows nothing about the greenthreads.
44+
this is mostly copied over from the libraft stuff.
45+
46+
libptimc is a greenthreads implementation using libimc to determine thread
47+
ordering.
48+
49+
#### Usage
50+
51+
Write your code inside a function called
52+
53+
void imc_check_main(void)
54+
55+
Use imcthread_create and imcthread_yield
56+
57+
#### Random Notes
58+
- imcthread_create does *not* act as a yield

Diff for: exhaust-libptimc/examples/5threads.c

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "libptimc.h"
2+
#include <assert.h>
3+
4+
void *worker(void *_) {
5+
// printf("Running worker %llu\n", (size_t)_);
6+
return 0;
7+
}
8+
9+
void imc_check_main(void) {
10+
imcthread_t ts[10];
11+
int n = (sizeof ts) / (sizeof ts[0]);
12+
for (size_t i = 0; i < n; i++)
13+
imcthread_create(&ts[i], NULL, worker, (void *)i);
14+
15+
for (size_t i = 0; i < n; i++) {
16+
// printf("Starting the join on %llu\n", i);
17+
imcthread_join(ts[i], NULL);
18+
// printf("Finished the join on %llu\n", i);
19+
}
20+
// printf("Done!\n");
21+
// printf("---------------------\n");
22+
}

Diff for: exhaust-libptimc/examples/global_state.c

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "libptimc.h"
2+
#include <stdint.h>
3+
4+
static uint64_t counter;
5+
6+
static void resetter(void) {
7+
counter = 0;
8+
}
9+
10+
void *worker(void *_) {
11+
uint64_t cval = counter;
12+
// imcthread_yield();
13+
cval += UINT32_MAX;
14+
// imcthread_yield();
15+
counter = cval;
16+
return NULL;
17+
}
18+
19+
void *sub_worker(void *_) {
20+
counter += UINT32_MAX;
21+
counter -= UINT32_MAX;
22+
return NULL;
23+
}
24+
25+
void imc_check_main(void) {
26+
register_resetter(resetter);
27+
28+
printf("Starting main; counter value is %llu\n", counter);
29+
30+
imcthread_t t1, t2, t3;
31+
imcthread_create(&t1, NULL, worker, 0);
32+
imcthread_create(&t2, NULL, worker, 0);
33+
imcthread_create(&t3, NULL, sub_worker, 0);
34+
35+
imcthread_join(t1, NULL);
36+
imcthread_join(t2, NULL);
37+
imcthread_join(t3, NULL);
38+
39+
assert(counter == ((uint64_t)UINT32_MAX) + ((uint64_t)UINT32_MAX));
40+
}

Diff for: exhaust-libptimc/examples/hashing.c

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "libptimc.h"
2+
#include <assert.h>
3+
4+
void imc_check_main(void) {
5+
int counter = 0;
6+
for (int i = 0; i < 5; i++) {
7+
counter += choose(2, (counter << 8) | i);
8+
assert(counter < 6);
9+
}
10+
}

Diff for: exhaust-libptimc/examples/simple_race.c

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include "libptimc.h"
2+
#include <assert.h>
3+
4+
static int COUNTER = 0;
5+
6+
static void resetter(void) {
7+
COUNTER = 0;
8+
}
9+
10+
void *worker(void *_) {
11+
int local_counter = COUNTER;
12+
local_counter++;
13+
imcthread_yield();
14+
COUNTER = local_counter;
15+
}
16+
17+
void imc_check_main(void) {
18+
register_resetter(resetter);
19+
20+
imcthread_t t1;
21+
imcthread_t t2;
22+
// printf("About to create two threads!\n");
23+
imcthread_create(&t1, NULL, worker, 0);
24+
imcthread_create(&t2, NULL, worker, 0);
25+
// printf("Threads created!\n");
26+
27+
imcthread_join(t1, NULL);
28+
imcthread_join(t2, NULL);
29+
30+
assert(COUNTER == 2);
31+
}

Diff for: exhaust-libptimc/libimc/README

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
###### Using the Core IMC Library ######
2+
The IMC library header is `libimc.h`. It looks like:
3+
4+
int choose(int n, size_t hash);
5+
void report_error();
6+
void check_exit_normal();
7+
extern void check_main();
8+
9+
Basic usage:
10+
11+
1. You write your test harness in a function called "check_main."
12+
2. Whenever you want to make a nondeterministic choice, call `choose(n,h)`
13+
where `n` is the number of possible choices (must be greater than 0) and
14+
`h` is a hash of the current state (or 0 to disable state hashing)
15+
3. When you encounter an error, call report_error()
16+
17+
A small example is shown in `imc_wrapper/test.c`:
18+
19+
#include <stdio.h>
20+
#include "../libimc/libimc.h"
21+
22+
void check_main() {
23+
int sum = 1;
24+
for (int i = 0; i < 6; i++) {
25+
sum += choose(7, ((size_t)sum << 3) + i);
26+
if (sum == 37) report_error();
27+
}
28+
return;
29+
}
30+
31+
It should report an error:
32+
33+
$ ./build/test
34+
Found an error with path: 6 6 6 6 6 6
35+
36+
You can trigger a replay of that error like so:
37+
38+
$ ./build/test 6 6 6 6 6 6
39+
$ echo $?
40+
1

Diff for: exhaust-libptimc/libimc/_libimc.h

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#pragma once
2+
3+
#define _GNU_SOURCE
4+
#include <fcntl.h>
5+
#include <unistd.h>
6+
#include <errno.h>
7+
#include "libimc.h"
8+
9+
extern int MASTER2WORKER_RD[N_WORKERS];
10+
extern int MASTER2WORKER_WR[N_WORKERS];
11+
extern int WORKER2MASTER_RD[N_WORKERS];
12+
extern int WORKER2MASTER_WR[N_WORKERS];
13+
14+
extern void launch_worker(unsigned int i);
15+
16+
enum message_type {
17+
MSG_NONE,
18+
19+
// Worker -> Master
20+
MSG_CAN_I_DIE,
21+
MSG_DID_SPLIT,
22+
MSG_NO_SPLIT,
23+
24+
// Master -> Worker
25+
MSG_PLEASE_SPLIT,
26+
MSG_OK_DIE,
27+
MSG_NO_DIE,
28+
};
29+
30+
struct message {
31+
enum message_type message_type;
32+
int new_id;
33+
int pid;
34+
size_t count;
35+
};
36+
37+
static void tell_pipe(int fd, uint8_t *ptr, size_t n) {
38+
while (n) {
39+
ssize_t n_sent = write(fd, ptr, n);
40+
if (n_sent == EAGAIN || n_sent == EWOULDBLOCK) n_sent = 0;
41+
ptr += n_sent;
42+
n -= n_sent;
43+
}
44+
}
45+
46+
static void hear_pipe(int fd, uint8_t *ptr, size_t n) {
47+
int any = 0;
48+
while (n) {
49+
ssize_t n_read = read(fd, ptr, n);
50+
if (n_read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
51+
n_read = 0;
52+
ptr += n_read;
53+
n -= n_read;
54+
if (!any && !n_read) return;
55+
any = 1;
56+
}
57+
}
58+
59+
static void tell_worker(struct message message, int idx) {
60+
tell_pipe(MASTER2WORKER_WR[idx], (uint8_t*)&message, sizeof(message));
61+
}
62+
63+
static void tell_master(struct message message, int idx) {
64+
tell_pipe(WORKER2MASTER_WR[idx], (uint8_t*)&message, sizeof(message));
65+
}
66+
67+
static struct message hear_worker(int idx) {
68+
struct message message = {0};
69+
hear_pipe(WORKER2MASTER_RD[idx], (uint8_t*)&message, sizeof(message));
70+
return message;
71+
}
72+
73+
static struct message hear_master(int idx) {
74+
struct message message = {0};
75+
hear_pipe(MASTER2WORKER_RD[idx], (uint8_t*)&message, sizeof(message));
76+
return message;
77+
}

Diff for: exhaust-libptimc/libimc/libimc.h

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
#include <stdio.h>
5+
#include <assert.h>
6+
#include <string.h>
7+
#include <stdlib.h>
8+
#include <stddef.h>
9+
10+
typedef uint8_t choice_t;
11+
typedef uint64_t hash_t;
12+
13+
choice_t choose(choice_t n, hash_t hash);
14+
void report_error();
15+
void check_exit_normal();
16+
extern void check_main();
17+
18+
void register_resetter(void (*resetter)(void));

Diff for: exhaust-libptimc/libimc/libimc_linux.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "_libimc.h"
2+
#include <signal.h>
3+
#include <unistd.h>
4+
#include <fcntl.h>
5+
#include <stdlib.h>
6+
#include <sys/wait.h>
7+
#include <time.h>
8+
9+
extern void launch_replay(choice_t *path);

0 commit comments

Comments
 (0)