|
0
|
1 //========================================================================== |
|
|
2 // |
|
2
|
3 // sync/cnt_sem.cxx |
|
0
|
4 // |
|
2
|
5 // Counting semaphore implementation |
|
0
|
6 // |
|
|
7 //========================================================================== |
|
|
8 //####COPYRIGHTBEGIN#### |
|
|
9 // |
|
|
10 // ------------------------------------------- |
|
|
11 // The contents of this file are subject to the Cygnus eCos Public License |
|
|
12 // Version 1.0 (the "License"); you may not use this file except in |
|
|
13 // compliance with the License. You may obtain a copy of the License at |
|
|
14 // http://sourceware.cygnus.com/ecos |
|
|
15 // |
|
|
16 // Software distributed under the License is distributed on an "AS IS" |
|
|
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the |
|
|
18 // License for the specific language governing rights and limitations under |
|
|
19 // the License. |
|
|
20 // |
|
|
21 // The Original Code is eCos - Embedded Cygnus Operating System, released |
|
|
22 // September 30, 1998. |
|
|
23 // |
|
|
24 // The Initial Developer of the Original Code is Cygnus. Portions created |
|
2
|
25 // by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions. All Rights Reserved. |
|
0
|
26 // ------------------------------------------- |
|
|
27 // |
|
|
28 //####COPYRIGHTEND#### |
|
|
29 //========================================================================== |
|
|
30 //#####DESCRIPTIONBEGIN#### |
|
|
31 // |
|
2
|
32 // Author(s): nickg |
|
|
33 // Contributors: nickg |
|
|
34 // Date: 1997-09-24 |
|
|
35 // Purpose: Cyg_Counting_Semaphore implementation |
|
|
36 // Description: This file contains the implementations of the counting semaphore |
|
0
|
37 // class. |
|
|
38 // |
|
|
39 //####DESCRIPTIONEND#### |
|
|
40 // |
|
|
41 //========================================================================== |
|
|
42 |
|
|
43 #include <pkgconf/kernel.h> |
|
|
44 |
|
|
45 #include <cyg/kernel/ktypes.h> // base kernel types |
|
|
46 #include <cyg/infra/cyg_trac.h> // tracing macros |
|
|
47 #include <cyg/infra/cyg_ass.h> // assertion macros |
|
|
48 #include <cyg/kernel/instrmnt.h> // instrumentation |
|
|
49 |
|
|
50 #include <cyg/kernel/thread.inl> // Cyg_Thread inlines |
|
|
51 |
|
|
52 #include <cyg/kernel/sema.hxx> // our header |
|
|
53 |
|
|
54 #include <cyg/kernel/sched.inl> // scheduler inlines |
|
|
55 |
|
|
56 // ------------------------------------------------------------------------- |
|
|
57 // Constructor |
|
|
58 |
|
|
59 Cyg_Counting_Semaphore::Cyg_Counting_Semaphore( |
|
|
60 cyg_count32 init_count // Initial count value |
|
|
61 ) |
|
|
62 { |
|
|
63 count = init_count; |
|
|
64 } |
|
|
65 |
|
|
66 // ------------------------------------------------------------------------- |
|
|
67 // Destructor |
|
|
68 |
|
|
69 Cyg_Counting_Semaphore::~Cyg_Counting_Semaphore() |
|
|
70 { |
|
|
71 CYG_ASSERT( queue.empty(), "Destroying semaphore with waiting threads"); |
|
|
72 } |
|
|
73 |
|
|
74 // ------------------------------------------------------------------------- |
|
|
75 // Wait until the count can be decremented without it becoming |
|
|
76 // negative. |
|
|
77 |
|
|
78 cyg_bool Cyg_Counting_Semaphore::wait() |
|
|
79 { |
|
|
80 cyg_bool result = true; |
|
|
81 Cyg_Thread *self = Cyg_Thread::self(); |
|
|
82 |
|
|
83 // Prevent preemption |
|
|
84 Cyg_Scheduler::lock(); |
|
|
85 |
|
|
86 CYG_INSTRUMENT_CNTSEM( CLAIM, this, count ); |
|
|
87 |
|
|
88 while( count == 0 && result ) |
|
|
89 { |
|
|
90 self->set_sleep_reason( Cyg_Thread::WAIT ); |
|
|
91 |
|
|
92 self->sleep(); |
|
|
93 |
|
|
94 queue.enqueue( self ); |
|
|
95 |
|
|
96 CYG_INSTRUMENT_CNTSEM( WAIT, this, 0 ); |
|
|
97 |
|
|
98 CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); |
|
|
99 |
|
|
100 Cyg_Scheduler::unlock(); |
|
|
101 Cyg_Scheduler::lock(); |
|
|
102 |
|
|
103 CYG_INSTRUMENT_CNTSEM( WOKE, this, count ); |
|
|
104 |
|
|
105 switch( self->get_wake_reason() ) |
|
|
106 { |
|
|
107 case Cyg_Thread::DESTRUCT: |
|
|
108 case Cyg_Thread::BREAK: |
|
|
109 result = false; |
|
|
110 break; |
|
|
111 |
|
|
112 case Cyg_Thread::EXIT: |
|
|
113 self->exit(); |
|
|
114 break; |
|
|
115 |
|
|
116 default: |
|
|
117 break; |
|
|
118 } |
|
|
119 } |
|
|
120 |
|
|
121 if( result ) count--; |
|
|
122 |
|
|
123 // Unlock the scheduler |
|
|
124 Cyg_Scheduler::unlock(); |
|
|
125 |
|
|
126 return result; |
|
|
127 } |
|
|
128 |
|
|
129 // ------------------------------------------------------------------------- |
|
|
130 // Wait until the count can be decremented without it becoming |
|
|
131 // negative. |
|
|
132 |
|
|
133 #ifdef CYGFUN_KERNEL_THREADS_TIMER |
|
|
134 |
|
|
135 cyg_bool |
|
|
136 Cyg_Counting_Semaphore::wait( cyg_tick_count timeout ) |
|
|
137 { |
|
|
138 cyg_bool result = true; |
|
|
139 Cyg_Thread *self = Cyg_Thread::self(); |
|
|
140 |
|
|
141 // Prevent preemption |
|
|
142 Cyg_Scheduler::lock(); |
|
|
143 |
|
|
144 CYG_INSTRUMENT_CNTSEM( CLAIM, this, count ); |
|
|
145 |
|
|
146 // Set the timer _once_ outside the loop. |
|
|
147 self->set_timer( timeout, Cyg_Thread::TIMEOUT ); |
|
|
148 |
|
|
149 // If the timeout is in the past, the wake reason will have been |
|
|
150 // set to something other than NONE already. Set the result false |
|
|
151 // to force an immediate return. |
|
|
152 |
|
|
153 if( self->get_wake_reason() != Cyg_Thread::NONE ) |
|
|
154 result = false; |
|
|
155 |
|
|
156 while ( 0 == count && result ) { |
|
|
157 |
|
|
158 // must reset the sleep reason every time |
|
|
159 self->set_sleep_reason( Cyg_Thread::TIMEOUT ); |
|
|
160 |
|
|
161 self->sleep(); |
|
|
162 |
|
|
163 queue.enqueue( self ); |
|
|
164 |
|
|
165 CYG_INSTRUMENT_CNTSEM( WAIT, this, 0 ); |
|
|
166 |
|
|
167 CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, |
|
|
168 "Called with non-zero scheduler lock"); |
|
|
169 |
|
|
170 Cyg_Scheduler::unlock(); |
|
|
171 Cyg_Scheduler::lock(); |
|
|
172 |
|
|
173 CYG_INSTRUMENT_CNTSEM( WOKE, this, count ); |
|
|
174 |
|
|
175 switch( self->get_wake_reason() ) |
|
|
176 { |
|
|
177 case Cyg_Thread::TIMEOUT: |
|
|
178 result = false; |
|
|
179 CYG_INSTRUMENT_CNTSEM( TIMEOUT, this, count); |
|
|
180 break; |
|
|
181 |
|
|
182 case Cyg_Thread::DESTRUCT: |
|
|
183 case Cyg_Thread::BREAK: |
|
|
184 result = false; |
|
|
185 break; |
|
|
186 |
|
|
187 case Cyg_Thread::EXIT: |
|
|
188 self->exit(); |
|
|
189 break; |
|
|
190 |
|
|
191 default: |
|
|
192 break; |
|
|
193 } |
|
|
194 } |
|
|
195 |
|
|
196 // Clear the timeout. It is irrelevant whether the alarm has |
|
|
197 // actually gone off or not. |
|
|
198 self->clear_timer(); |
|
|
199 |
|
|
200 if ( result ) count--; |
|
|
201 |
|
|
202 // Unlock the scheduler and maybe switch threads |
|
|
203 Cyg_Scheduler::unlock(); |
|
|
204 |
|
|
205 return result; |
|
|
206 } |
|
|
207 |
|
|
208 #endif // CYGFUN_KERNEL_THREADS_TIMER |
|
|
209 |
|
|
210 // ------------------------------------------------------------------------- |
|
|
211 // Try to decrement, but fail if not possible |
|
|
212 |
|
|
213 cyg_bool Cyg_Counting_Semaphore::trywait() |
|
|
214 { |
|
|
215 cyg_bool result = true; |
|
|
216 |
|
|
217 // Prevent preemption |
|
|
218 Cyg_Scheduler::lock(); |
|
|
219 |
|
|
220 if( count > 0 ) count--; |
|
|
221 else result = false; |
|
|
222 |
|
|
223 CYG_INSTRUMENT_CNTSEM( TRY, this, result ); |
|
|
224 |
|
|
225 // Unlock the scheduler and maybe switch threads |
|
|
226 Cyg_Scheduler::unlock(); |
|
|
227 |
|
|
228 return result; |
|
|
229 } |
|
|
230 |
|
|
231 // ------------------------------------------------------------------------- |
|
|
232 // Increment count |
|
|
233 |
|
|
234 void Cyg_Counting_Semaphore::post() |
|
|
235 { |
|
|
236 // Prevent preemption |
|
|
237 Cyg_Scheduler::lock(); |
|
|
238 |
|
|
239 CYG_INSTRUMENT_CNTSEM( POST, this, 0 ); |
|
|
240 |
|
|
241 count++; |
|
|
242 |
|
|
243 if( !queue.empty() ) { |
|
|
244 |
|
|
245 // The queue is non-empty, so grab the next |
|
|
246 // thread from it and wake it up. The waiter |
|
|
247 // will decrement the count when he is awakened. |
|
|
248 |
|
|
249 Cyg_Thread *thread = queue.dequeue(); |
|
|
250 |
|
|
251 thread->set_wake_reason( Cyg_Thread::DONE ); |
|
|
252 |
|
|
253 thread->wake(); |
|
|
254 |
|
|
255 CYG_INSTRUMENT_CNTSEM( WAKE, this, thread ); |
|
|
256 } |
|
|
257 |
|
|
258 // Unlock the scheduler and maybe switch threads |
|
|
259 Cyg_Scheduler::unlock(); |
|
|
260 } |
|
|
261 |
|
|
262 // ------------------------------------------------------------------------- |
|
|
263 // Get current count value |
|
|
264 |
|
|
265 cyg_count32 Cyg_Counting_Semaphore::peek() |
|
|
266 { |
|
|
267 // This is a single read of the value of count. |
|
|
268 // This is already atomic, hence there is no need |
|
|
269 // to lock the scheduler. |
|
|
270 |
|
|
271 return count; |
|
|
272 } |
|
|
273 |
|
|
274 // ------------------------------------------------------------------------- |
|
|
275 // EOF sync/cnt_sem.cxx |