summaryrefslogtreecommitdiff
path: root/kern/sync.c
blob: 4e4e6de3eaa103ec693af8c026167c7cc57ec2ae (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
/** @file sync.c
 *
 *  @brief locks.
 *
 *  @author Hanjie Wu (hanjiew)
 *  @bug No functional bugs
 */

#include <x86/asm.h>

#include <interrupt.h>
#include <sched.h>
#include <sync.h>

void mutex_lock(mutex_t* m) {
    thread_t* current = get_current();
    int old_if = spl_lock(&m->guard);
    if (m->locked == 0) {
        m->locked = 1;
        spl_unlock(&m->guard, old_if);
        return;
    }
    queue_insert_tail(&m->waiters, &current->sched_link);
    current->status = THREAD_BLOCKED;
    int old_if2 = spl_lock(&ready_lock);
    thread_t* t = select_next();
    spl_unlock(&ready_lock, old_if2);
    /* release m->gaurd last, otherwise other thread may switch to us before
     * yielding
     */
    yield_to_spl_unlock(t, &m->guard, old_if);
}

void mutex_unlock(mutex_t* m) {
    int old_if = spl_lock(&m->guard);
    if (m->waiters == NULL) {
        m->locked = 0;
        spl_unlock(&m->guard, old_if);
        return;
    }
    /* transfer lock ownership to t */
    thread_t* t =
        queue_data(queue_remove_head(&m->waiters), thread_t, sched_link);
    int old_if2 = spl_lock(&ready_lock);
    insert_ready_head(t);
    spl_unlock(&ready_lock, old_if2);
    spl_unlock(&m->guard, old_if);
}

void cv_wait(cv_t* cv, mutex_t* m) {
    thread_t* current = get_current();
    int old_if = spl_lock(&cv->guard);
    queue_insert_tail(&cv->waiters, &current->sched_link);
    current->status = THREAD_BLOCKED;
    mutex_unlock(m);
    int old_if2 = spl_lock(&ready_lock);
    thread_t* t = select_next();
    spl_unlock(&ready_lock, old_if2);
    yield_to_spl_unlock(t, &cv->guard, old_if);
    /* some threads may jump in so a condition check in a while loop is required
     * outside
     */
    mutex_lock(m);
}

void cv_signal(cv_t* cv) {
    int old_if = spl_lock(&cv->guard);
    if (cv->waiters == NULL) {
        spl_unlock(&cv->guard, old_if);
        return;
    }
    thread_t* t =
        queue_data(queue_remove_head(&cv->waiters), thread_t, sched_link);
    int old_if2 = spl_lock(&ready_lock);
    insert_ready_head(t);
    spl_unlock(&ready_lock, old_if2);
    spl_unlock(&cv->guard, old_if);
}