1 /*
2 * Copyright (c) 2023 Ali Mashtizadeh
3 * All rights reserved.
4 */
5
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <sys/cdefs.h>
11 #include <sys/kassert.h>
12 #include <sys/kdebug.h>
13 #include <sys/queue.h>
14 #include <sys/thread.h>
15 #include <sys/spinlock.h>
16 #include <sys/waitchannel.h>
17
18 Spinlock chanListLock;
19 LIST_HEAD(ChanListHead, WaitChannel) chanList = LIST_HEAD_INITIALIZER(chanList);
20
21 void
WaitChannel_EarlyInit()22 WaitChannel_EarlyInit()
23 {
24 Spinlock_Init(&chanListLock, "WaitChannel List", SPINLOCK_TYPE_NORMAL);
25 }
26
27 void
WaitChannel_Init(WaitChannel * wchan,const char * name)28 WaitChannel_Init(WaitChannel *wchan, const char *name)
29 {
30 TAILQ_INIT(&wchan->chanQueue);
31 strncpy(&wchan->name[0], name, WAITCHANNEL_NAMELEN);
32 Spinlock_Init(&wchan->lock, name, SPINLOCK_TYPE_NORMAL);
33
34 Spinlock_Lock(&chanListLock);
35 LIST_INSERT_HEAD(&chanList, wchan, chanList);
36 Spinlock_Unlock(&chanListLock);
37 }
38
39 void
WaitChannel_Destroy(WaitChannel * wchan)40 WaitChannel_Destroy(WaitChannel *wchan)
41 {
42 ASSERT(TAILQ_EMPTY(&wchan->chanQueue));
43
44 Spinlock_Lock(&chanListLock);
45 LIST_REMOVE(wchan, chanList);
46 Spinlock_Unlock(&chanListLock);
47
48 Spinlock_Destroy(&wchan->lock);
49 }
50
51 /**
52 * WaitChannel_Lock --
53 *
54 * Acquires the wait channel lock.
55 */
56 void
WaitChannel_Lock(WaitChannel * wchan)57 WaitChannel_Lock(WaitChannel *wchan)
58 {
59 Spinlock_Lock(&wchan->lock);
60 }
61
62 /**
63 * WaitChannel_Sleep --
64 *
65 * Places the current thread to asleep while releasing the wait channel lock.
66 *
67 * Side Effect:
68 * Retains a reference to thread until the thread is woken up.
69 */
70 void
WaitChannel_Sleep(WaitChannel * wchan)71 WaitChannel_Sleep(WaitChannel *wchan)
72 {
73 Thread *thr = Sched_Current();
74
75 Sched_SetWaiting(thr);
76 TAILQ_INSERT_TAIL(&wchan->chanQueue, thr, chanQueue);
77 Spinlock_Unlock(&wchan->lock);
78
79 Sched_Scheduler();
80 }
81
82 /**
83 * WaitChannel_Wake --
84 *
85 * Wake up a single thread.
86 *
87 * Side Effects:
88 * Releases the thread reference once complete.
89 */
90 void
WaitChannel_Wake(WaitChannel * wchan)91 WaitChannel_Wake(WaitChannel *wchan)
92 {
93 Thread *thr;
94
95 Spinlock_Lock(&wchan->lock);
96
97 thr = TAILQ_FIRST(&wchan->chanQueue);
98 if (thr != NULL) {
99 TAILQ_REMOVE(&wchan->chanQueue, thr, chanQueue);
100 Sched_SetRunnable(thr);
101 Thread_Release(thr);
102 }
103
104 Spinlock_Unlock(&wchan->lock);
105 }
106
107 /**
108 * WaitChannel_WakeAll --
109 *
110 * Wakes up all threads currently sleeping on the wait channel.
111 *
112 * Side Effects:
113 * Releases all thread references.
114 */
115 void
WaitChannel_WakeAll(WaitChannel * wchan)116 WaitChannel_WakeAll(WaitChannel *wchan)
117 {
118 Thread *thr;
119 Thread *thrTemp;
120
121 Spinlock_Lock(&wchan->lock);
122
123 TAILQ_FOREACH_SAFE(thr, &wchan->chanQueue, chanQueue, thrTemp) {
124 TAILQ_REMOVE(&wchan->chanQueue, thr, chanQueue);
125 Sched_SetRunnable(thr);
126 Thread_Release(thr);
127 }
128
129 Spinlock_Unlock(&wchan->lock);
130 }
131
132