1 /*
2  * Copyright (c) 2013-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/kassert.h>
11 #include <sys/kconfig.h>
12 #include <sys/kdebug.h>
13 #include <sys/ktime.h>
14 #include <sys/mp.h>
15 #include <sys/spinlock.h>
16 #include <sys/semaphore.h>
17 #include <sys/thread.h>
18 
19 Spinlock semaListLock = { 0, 0, 0, 0, 0, 0, 0, 0, "Semaphore List" };
20 LIST_HEAD(SemaListHead, Semaphore) semaList = LIST_HEAD_INITIALIZER(semaList);
21 
22 extern uint64_t ticksPerSecond;
23 
24 void
Semaphore_Init(Semaphore * sema,int count,const char * name)25 Semaphore_Init(Semaphore *sema, int count, const char *name)
26 {
27     Spinlock_Init(&sema->lock, name, SPINLOCK_TYPE_NORMAL);
28     sema->count = count;
29 
30     strncpy(&sema->name[0], name, SEMAPHORE_NAMELEN);
31     TAILQ_INIT(&sema->waiters);
32 
33     Spinlock_Lock(&semaListLock);
34     LIST_INSERT_HEAD(&semaList, sema, semaphoreList);
35     Spinlock_Unlock(&semaListLock);
36 }
37 
38 void
Semaphore_Destroy(Semaphore * sema)39 Semaphore_Destroy(Semaphore *sema)
40 {
41     Spinlock_Lock(&semaListLock);
42     LIST_REMOVE(sema, semaphoreList);
43     Spinlock_Unlock(&semaListLock);
44 
45     Spinlock_Destroy(&sema->lock);
46 }
47 
48 void
Semaphore_Acquire(Semaphore * sema)49 Semaphore_Acquire(Semaphore *sema)
50 {
51     Thread *cur = Sched_Current();
52 
53     while (1) {
54 	Spinlock_Lock(&sema->lock);
55 	if (sema->count > 0) {
56 	    sema->count -= 1;
57 	    Spinlock_Unlock(&sema->lock);
58 	    Thread_Release(cur);
59 	    return;
60 	}
61 
62 	// Add to sleeper list
63 	TAILQ_INSERT_TAIL(&sema->waiters, cur, semaQueue);
64 	Sched_SetWaiting(cur);
65 
66 	Spinlock_Unlock(&sema->lock);
67 	Sched_Scheduler();
68     }
69 }
70 
71 void
Semaphore_Release(Semaphore * sema)72 Semaphore_Release(Semaphore *sema)
73 {
74     Thread *thr;
75 
76     Spinlock_Lock(&sema->lock);
77     sema->count += 1;
78 
79     // Wakeup thread
80     thr = TAILQ_FIRST(&sema->waiters);
81     if (thr != NULL) {
82 	TAILQ_REMOVE(&sema->waiters, thr, semaQueue);
83 	Sched_SetRunnable(thr);
84     }
85     Spinlock_Unlock(&sema->lock);
86 }
87 
88 bool
Semaphore_TryAcquire(Semaphore * sema)89 Semaphore_TryAcquire(Semaphore *sema)
90 {
91     Spinlock_Lock(&sema->lock);
92     if (sema->count > 0) {
93 	sema->count -= 1;
94 	Spinlock_Unlock(&sema->lock);
95 	return true;
96     }
97     Spinlock_Unlock(&sema->lock);
98     return false;
99 }
100 
101 void
Debug_Semaphores(int argc,const char * argv[])102 Debug_Semaphores(int argc, const char *argv[])
103 {
104     Semaphore *sema;
105 
106     Spinlock_Lock(&semaListLock);
107 
108     kprintf("%-36s   Count\n", "Lock Name");
109     LIST_FOREACH(sema, &semaList, semaphoreList)
110     {
111 	Thread *thr;
112 	kprintf("%-36s %8d\n", sema->name, sema->count);
113 	TAILQ_FOREACH(thr, &sema->waiters, semaQueue) {
114 	    kprintf("waiting: %d:%d\n", thr->proc->pid, thr->tid);
115 	}
116     }
117 
118     Spinlock_Unlock(&semaListLock);
119 }
120 
121 REGISTER_DBGCMD(semaphores, "Display list of semaphores", Debug_Semaphores);
122 
123