summaryrefslogtreecommitdiff
path: root/user/libthread/rwlock.c
blob: 4535d9b372458c01d5f3494040e3cae793567120 (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
/**
 * @file rwlock.c
 * @author Hanjie Wu (hanjiew)
 * @author Devang Acharya (devanga)
 * @brief RW Lock implementation
 * @date 2023-02-22
 *
 */

#include <assert.h>
#include <rwlock.h>

/**
 * @brief initialize the lock pointed to by rwlock
 *
 * @param rwlock
 * @return int
 */
int rwlock_init(rwlock_t* rwlock) {
    rwlock->status = RWLOCK_STATUS_UNLOCK;
    rwlock->num_reader = 0;
    mutex_init(&rwlock->status_lock);
    cond_init(&rwlock->reader_cv);
    cond_init(&rwlock->writer_cv);
    return 0;
}

/**
 * @brief lock a rwlock
 */
void rwlock_lock(rwlock_t* rwlock, int type) {
    mutex_lock(&rwlock->status_lock);
    if (type == RWLOCK_READ) {
        while (rwlock->status == RWLOCK_STATUS_WRITE) {
            /* wait for the write to release */
            cond_wait(&rwlock->reader_cv, &rwlock->status_lock);
        }
        rwlock->status = RWLOCK_STATUS_READ;
        rwlock->num_reader++;
    } else {
        while (rwlock->status != RWLOCK_STATUS_UNLOCK) {
            affirm(rwlock->status == RWLOCK_STATUS_WRITE);
            /* wait for readers to release */
            cond_wait(&rwlock->writer_cv, &rwlock->status_lock);
        }
        rwlock->status = RWLOCK_STATUS_WRITE;
    }
    mutex_unlock(&rwlock->status_lock);
}

/**
 * @brief unlock a rwlock
 */
void rwlock_unlock(rwlock_t* rwlock) {
    mutex_lock(&rwlock->status_lock);
    if (rwlock->status == RWLOCK_STATUS_READ) {
        rwlock->num_reader--;
        /* the last reader should notify the writers */
        if (rwlock->num_reader == 0) {
            rwlock->status = RWLOCK_STATUS_UNLOCK;
            cond_broadcast(&rwlock->writer_cv);
        }
    } else {
        affirm(rwlock->status == RWLOCK_STATUS_WRITE);
        rwlock->status = RWLOCK_STATUS_UNLOCK;
        /* notify all readers */
        cond_broadcast(&rwlock->reader_cv);
    }
    mutex_unlock(&rwlock->status_lock);
}

/**
 * @brief destroy a rwlock
 */
void rwlock_destroy(rwlock_t* rwlock) {
    affirm(rwlock->status == RWLOCK_STATUS_UNLOCK);
    mutex_destroy(&rwlock->status_lock);
    cond_destroy(&rwlock->reader_cv);
    cond_destroy(&rwlock->writer_cv);
}

/**
 * @brief downgrade a rwlock
 */
void rwlock_downgrade(rwlock_t* rwlock) {
    affirm(rwlock->status == RWLOCK_STATUS_WRITE);
    rwlock->status = RWLOCK_STATUS_READ;
    cond_broadcast(&rwlock->reader_cv);
}