7902c6107e5fcbf364be25097b4d7498534f8213
[akaros.git] / user / parlib / mutex.c
1 /* Copyright (c) 2016 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 /* Generic Uthread Mutexes.  2LSs implement their own methods, but we need a
6  * 2LS-independent interface and default implementation. */
7
8 #include <parlib/uthread.h>
9 #include <sys/queue.h>
10 #include <parlib/spinlock.h>
11 #include <malloc.h>
12
13 struct uth_default_mtx;
14 struct uth_mtx_link {
15         TAILQ_ENTRY(uth_mtx_link)       next;
16         struct uth_default_mtx          *mtx;
17         struct uthread                          *uth;
18 };
19
20 struct uth_default_mtx {
21         struct spin_pdr_lock            lock;
22         TAILQ_HEAD(t, uth_mtx_link)     waiters;
23         bool                                            locked;
24 };
25
26 static struct uth_default_mtx *uth_default_mtx_alloc(void)
27 {
28         struct uth_default_mtx *mtx;
29
30         mtx = malloc(sizeof(struct uth_default_mtx));
31         assert(mtx);
32         spin_pdr_init(&mtx->lock);
33         TAILQ_INIT(&mtx->waiters);
34         mtx->locked = FALSE;
35         return mtx;
36 }
37
38 static void uth_default_mtx_free(struct uth_default_mtx *mtx)
39 {
40         assert(TAILQ_EMPTY(&mtx->waiters));
41         free(mtx);
42 }
43
44 static void __mutex_cb(struct uthread *uth, void *arg)
45 {
46         struct uth_mtx_link *link = (struct uth_mtx_link*)arg;
47         struct uth_default_mtx *mtx = link->mtx;
48
49         /* We need to tell the 2LS that its thread blocked.  We need to do this
50          * before unlocking the mtx, since as soon as we unlock, the mtx could be
51          * released and our thread restarted.
52          *
53          * Also note the lock-ordering rule.  The mtx lock is grabbed before any
54          * locks the 2LS might grab. */
55         uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX);
56         spin_pdr_unlock(&mtx->lock);
57 }
58
59 static void uth_default_mtx_lock(struct uth_default_mtx *mtx)
60 {
61         struct uth_mtx_link link;
62
63         spin_pdr_lock(&mtx->lock);
64         if (!mtx->locked) {
65                 mtx->locked = TRUE;
66                 spin_pdr_unlock(&mtx->lock);
67                 return;
68         }
69         link.mtx = mtx;
70         link.uth = current_uthread;
71         TAILQ_INSERT_TAIL(&mtx->waiters, &link, next);
72         /* the unlock is done in the yield callback.  as always, we need to do this
73          * part in vcore context, since as soon as we unlock the uthread could
74          * restart.  (atomically yield and unlock). */
75         uthread_yield(TRUE, __mutex_cb, &link);
76 }
77
78 static void uth_default_mtx_unlock(struct uth_default_mtx *mtx)
79 {
80         struct uth_mtx_link *first;
81
82         spin_pdr_lock(&mtx->lock);
83         first = TAILQ_FIRST(&mtx->waiters);
84         if (first)
85                 TAILQ_REMOVE(&mtx->waiters, first, next);
86         else
87                 mtx->locked = FALSE;
88         spin_pdr_unlock(&mtx->lock);
89         if (first)
90                 uthread_runnable(first->uth);
91 }
92
93 uth_mutex_t uth_mutex_alloc(void)
94 {
95         if (sched_ops->mutex_alloc)
96                 return sched_ops->mutex_alloc();
97         return (uth_mutex_t)uth_default_mtx_alloc();
98 }
99
100 void uth_mutex_free(uth_mutex_t m)
101 {
102         if (sched_ops->mutex_free) {
103                 sched_ops->mutex_free(m);
104                 return;
105         }
106         uth_default_mtx_free((struct uth_default_mtx*)m);
107 }
108
109 void uth_mutex_lock(uth_mutex_t m)
110 {
111         if (sched_ops->mutex_lock) {
112                 sched_ops->mutex_lock(m);
113                 return;
114         }
115         uth_default_mtx_lock((struct uth_default_mtx*)m);
116 }
117
118 void uth_mutex_unlock(uth_mutex_t m)
119 {
120         if (sched_ops->mutex_unlock) {
121                 sched_ops->mutex_unlock(m);
122                 return;
123         }
124         uth_default_mtx_unlock((struct uth_default_mtx*)m);
125 }