1+ #include " pch.h"
2+ #include < kf/Semaphore.h>
3+ #include < kf/Thread.h>
4+
5+ namespace
6+ {
7+ constexpr int kOneMillisecond = 1000 ;
8+
9+ struct Context
10+ {
11+ kf::Semaphore* semaphore = nullptr ;
12+ NTSTATUS status = STATUS_WAIT_1;
13+ };
14+
15+ void delay ()
16+ {
17+ LARGE_INTEGER timeout;
18+ timeout.QuadPart = -kOneMillisecond ;
19+ KeDelayExecutionThread (KernelMode, false , &timeout);
20+ }
21+ }
22+
23+ SCENARIO (" kf::Semaphore" )
24+ {
25+ GIVEN (" A semaphore with count = 1 and limit = 1" )
26+ {
27+ kf::Semaphore sem (1 , 1 );
28+ Context ctx{ &sem };
29+ kf::Thread thread;
30+
31+ WHEN (" Trying to acquire should succeed immediately" )
32+ {
33+ thread.start ([](void * context) {
34+ auto ctx = static_cast <Context*>(context);
35+ ctx->status = ctx->semaphore ->wait ();
36+ }, &ctx);
37+
38+ thread.join ();
39+
40+ THEN (" Thread doesn't wait" )
41+ {
42+ REQUIRE (ctx.status == STATUS_SUCCESS);
43+ }
44+ }
45+ }
46+
47+ GIVEN (" A semaphore with count = 0 and limit = 1" )
48+ {
49+ kf::Semaphore sem (0 , 1 );
50+
51+ WHEN (" Threads wait with 0" )
52+ {
53+ kf::Thread thread;
54+ Context ctx{ &sem };
55+
56+ thread.start ([](void * context) {
57+ auto ctx = static_cast <Context*>(context);
58+ LARGE_INTEGER timeout{ 0 };
59+ ctx->status = ctx->semaphore ->wait (&timeout);
60+ }, &ctx);
61+
62+ thread.join ();
63+
64+ THEN (" Thread should be timed out" )
65+ {
66+ REQUIRE (ctx.status == STATUS_TIMEOUT);
67+ }
68+ }
69+
70+ WHEN (" Threads wait with 100 miliseconds" )
71+ {
72+ kf::Thread thread;
73+ Context ctx{ &sem };
74+
75+ thread.start ([](void * context) {
76+ auto ctx = static_cast <Context*>(context);
77+ LARGE_INTEGER timeout;
78+ timeout.QuadPart = kOneMillisecond * -100 ;
79+ ctx->status = ctx->semaphore ->wait (&timeout);
80+ }, &ctx);
81+
82+ delay ();
83+
84+ THEN (" Thread should be waiting" )
85+ {
86+ REQUIRE (ctx.status == STATUS_WAIT_1);
87+ }
88+
89+ thread.join ();
90+
91+ THEN (" Thread should be timed out afted 100 milliseconds" )
92+ {
93+ REQUIRE (ctx.status == STATUS_TIMEOUT);
94+ }
95+ }
96+ }
97+
98+ GIVEN (" Semaphore with count = 0 and limit = 2" )
99+ {
100+ kf::Semaphore sem (0 , 2 );
101+ Context ctx1{ &sem }, ctx2{ &sem };
102+ kf::Thread thread1, thread2;
103+
104+ thread1.start ([](void * context) {
105+ auto ctx = static_cast <Context*>(context);
106+ ctx->status = ctx->semaphore ->wait ();
107+ }, &ctx1);
108+
109+ thread2.start ([](void * context) {
110+ auto ctx = static_cast <Context*>(context);
111+ ctx->status = ctx->semaphore ->wait ();
112+ }, &ctx2);
113+
114+ delay ();
115+
116+ THEN (" Both threads are waiting" )
117+ {
118+ REQUIRE (ctx1.status == STATUS_WAIT_1);
119+ REQUIRE (ctx2.status == STATUS_WAIT_1);
120+ }
121+
122+ WHEN (" We release semaphore with 2" )
123+ {
124+ sem.release (2 );
125+ thread1.join ();
126+ thread2.join ();
127+
128+ THEN (" Both threads should stop waiting" )
129+ {
130+ REQUIRE (ctx1.status == STATUS_SUCCESS);
131+ REQUIRE (ctx2.status == STATUS_SUCCESS);
132+ }
133+ }
134+ }
135+
136+ GIVEN (" Semaphore with count = 2 and limit = 2" )
137+ {
138+ kf::Semaphore sem (2 , 2 );
139+ Context ctx{ &sem };
140+ kf::Thread thread;
141+
142+ WHEN (" Semaphore released over limit twice" )
143+ {
144+ sem.release ();
145+ sem.release ();
146+
147+ THEN (" No crash occures" )
148+ {
149+ }
150+ }
151+ }
152+
153+ GIVEN (" Semaphore with count = 1 and limit = 1" )
154+ {
155+ kf::Semaphore sem (1 , 2 );
156+ Context ctx1{ &sem }, ctx2{ &sem }, ctx3{ &sem };
157+ kf::Thread thread1, thread2, thread3;
158+
159+ WHEN (" Semaphore is released twice, exceeding the limit" )
160+ {
161+ sem.release ();
162+ sem.release ();
163+
164+ THEN (" First two threads should succeed, third should wait" )
165+ {
166+ thread1.start ([](void * context) {
167+ auto ctx = static_cast <Context*>(context);
168+ ctx->status = ctx->semaphore ->wait ();
169+ }, &ctx1);
170+
171+ thread2.start ([](void * context) {
172+ auto ctx = static_cast <Context*>(context);
173+ ctx->status = ctx->semaphore ->wait ();
174+ }, &ctx2);
175+
176+ thread3.start ([](void * context) {
177+ auto ctx = static_cast <Context*>(context);
178+ ctx->status = ctx->semaphore ->wait ();
179+ }, &ctx3);
180+
181+ delay ();
182+
183+ int waitCount = ctx1.status != STATUS_SUCCESS + ctx2.status != STATUS_SUCCESS + ctx3.status != STATUS_SUCCESS;
184+ REQUIRE (waitCount == 1 );
185+
186+ sem.release ();
187+ thread1.join ();
188+ thread2.join ();
189+ thread3.join ();
190+
191+ THEN (" Third thread should succeed after release" )
192+ {
193+ REQUIRE (ctx3.status == STATUS_SUCCESS);
194+ }
195+ }
196+ }
197+ }
198+ }
0 commit comments