summaryrefslogtreecommitdiff
path: root/libptimc/libptimc.c
blob: 3cda04b9dd1ed588acf31c328dbf60cba88e714a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include "libptimc.h"

static struct imcthread **THREADS = 0;
static size_t N_THREADS = 0;
static struct imcthread *CURRENT_THREAD = 0;
static ucontext_t MASTER_CTX;

static void switch_to_thread(struct imcthread *thread);
static void imcthread_entry_point(int tid);

static void resetter() {
    for (int i = 0; i < N_THREADS; i++) {
        free(THREADS[i]->ctx.uc_stack.ss_sp);
        free(THREADS[i]);
    }
    free(THREADS);
    THREADS = 0;
    N_THREADS = 0;
    CURRENT_THREAD = 0;
}

int imcthread_create(imcthread_t *threadp,
                     imcthread_attr_t *attr,
                     void *(*start_routine)(void *),
                     void *arg) {
    assert(!attr);
    THREADS = realloc(THREADS, (++N_THREADS) * sizeof(THREADS[0]));
    THREADS[N_THREADS - 1] = calloc(1, sizeof(struct imcthread));

    struct imcthread *thread = THREADS[N_THREADS - 1];
    *threadp = thread;
    thread->fn = start_routine;
    thread->arg = arg;
    thread->state = THREAD_STATE_FETUS;
    thread->id = N_THREADS - 1;

    assert(!getcontext(&thread->ctx));
    thread->ctx.uc_stack.ss_size = 4 * 1024 * 1024;
    thread->ctx.uc_stack.ss_sp = malloc(thread->ctx.uc_stack.ss_size);
    thread->ctx.uc_link = 0;
    makecontext(&thread->ctx, imcthread_entry_point, 1, thread->id);

    return 0;
}

int imcthread_yieldh(hash_t hash) {
    // pauses the current thread and returns to the master thread
    assert(CURRENT_THREAD);
    struct imcthread *thread = CURRENT_THREAD;

    if (thread->state == THREAD_STATE_DEAD) {
        for (size_t i = 0; i < N_THREADS; i++)
            if (THREADS[i]->waiting_on == thread)
                THREADS[i]->waiting_on = 0;
    }

    CURRENT_THREAD = 0;
    swapcontext(&(thread->ctx), &MASTER_CTX);

    return 0;
}
int imcthread_yield(void) { imcthread_yieldh(0); }

static void *_imc_check_main(void *_) { imc_check_main(); return 0; }
void check_main() {
    register_resetter(resetter);

    imcthread_t _thread;
    imcthread_create(&_thread, NULL, _imc_check_main, NULL);

    while (1) {
        int n_alive = 0, n_avail = 0;
        for (int i = 0; i < N_THREADS; i++) {
            if (THREADS[i]->state == THREAD_STATE_DEAD) continue;
            n_alive++;
            if (THREADS[i]->waiting_on) continue;
            n_avail++;
        }
        // check that we're not in a deadlock
        if (!n_avail) { assert(!n_alive); break; }

        int count = choose(n_avail, 0);
        for (int i = 0; i < N_THREADS; i++) {
            if (THREADS[i]->state == THREAD_STATE_DEAD) continue;
            if (THREADS[i]->waiting_on) continue;
            if (count--) continue;
            switch_to_thread(THREADS[i]);
            break;
        }
    }
}

int imcthread_joinh(imcthread_t thread, void **retval, hash_t hash) {
    if (thread->state != THREAD_STATE_DEAD) {
        CURRENT_THREAD->waiting_on = thread;
        imcthread_yield();
    }

    if (retval) *retval = thread->retval;
    return 0;
}
int imcthread_join(imcthread_t thread, void **retval) {
    return imcthread_joinh(thread, retval, 0);
}

static void switch_to_thread(struct imcthread *thread) {
    assert(!CURRENT_THREAD);
    assert(thread->state != THREAD_STATE_DEAD);

    CURRENT_THREAD = thread;
    swapcontext(&MASTER_CTX, &(thread->ctx));
}

static void imcthread_entry_point(int tid) {
    struct imcthread *thread = THREADS[tid];
    thread->state = THREAD_STATE_ALIVE;
    CURRENT_THREAD = thread;

    void *result = thread->fn(thread->arg);

    thread->retval = result;
    thread->state = THREAD_STATE_DEAD;

    imcthread_yield();
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback