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