comparison packages/kernel/current/include/mboxt.inl @ 0:3111d98ba7b3 ecos-v1_1-release

Initial commit of eCos version 1.1
author jlarmour
date Tue, 11 May 1999 11:16:07 +0000
parents
children 443894e2e912
comparison
equal deleted inserted replaced
-1:000000000000 0:3111d98ba7b3
1 #ifndef CYGONCE_KERNEL_MBOXT_INL
2 #define CYGONCE_KERNEL_MBOXT_INL
3 //==========================================================================
4 //
5 // mboxt.inl
6 //
7 // Mboxt mbox template class implementation
8 //
9 //==========================================================================
10 //####COPYRIGHTBEGIN####
11 //
12 // -------------------------------------------
13 // The contents of this file are subject to the Cygnus eCos Public License
14 // Version 1.0 (the "License"); you may not use this file except in
15 // compliance with the License. You may obtain a copy of the License at
16 // http://sourceware.cygnus.com/ecos
17 //
18 // Software distributed under the License is distributed on an "AS IS"
19 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
20 // License for the specific language governing rights and limitations under
21 // the License.
22 //
23 // The Original Code is eCos - Embedded Cygnus Operating System, released
24 // September 30, 1998.
25 //
26 // The Initial Developer of the Original Code is Cygnus. Portions created
27 // by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved.
28 // -------------------------------------------
29 //
30 //####COPYRIGHTEND####
31 //==========================================================================
32 //#####DESCRIPTIONBEGIN####
33 //
34 // Author(s): hmt
35 // Contributors: hmt
36 // Date: 1998-02-10
37 // Purpose: Mboxt template implementation
38 // Description: This file contains the implementations of the mboxt
39 // template classes.
40 //
41 //####DESCRIPTIONEND####
42 //
43 //==========================================================================
44
45 #include <pkgconf/kernel.h>
46
47 #include <cyg/kernel/ktypes.h> // base kernel types
48 #include <cyg/infra/cyg_trac.h> // tracing macros
49 #include <cyg/infra/cyg_ass.h> // assertion macros
50 #include <cyg/kernel/instrmnt.h> // instrumentation
51
52 #include <cyg/kernel/mboxt.hxx> // our header
53
54 #include <cyg/kernel/thread.inl> // thread inlines
55 #include <cyg/kernel/sched.inl> // scheduler inlines
56 #include <cyg/kernel/clock.inl> // clock inlines
57
58 // -------------------------------------------------------------------------
59 // inline function for awakening waiting threads
60
61 template <class T, cyg_count32 QUEUE_SIZE>
62 inline void
63 Cyg_Mboxt<T,QUEUE_SIZE>::wakeup_waiter( Cyg_ThreadQueue &q )
64 {
65 if( !q.empty() ) {
66 // The queue is non-empty, so grab the next thread and wake it up.
67 Cyg_Thread *thread = q.dequeue();
68
69 CYG_ASSERTCLASS( thread, "Bad thread pointer");
70
71 thread->set_wake_reason( Cyg_Thread::DONE );
72 thread->wake();
73 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
74 }
75 }
76
77 // -------------------------------------------------------------------------
78 // Constructor
79
80 template <class T, cyg_count32 QUEUE_SIZE>
81 Cyg_Mboxt<T,QUEUE_SIZE>::Cyg_Mboxt()
82 {
83 CYG_REPORT_FUNCTION();
84 base = 0;
85 count = 0;
86 }
87
88 // -------------------------------------------------------------------------
89 // Destructor
90
91 template <class T, cyg_count32 QUEUE_SIZE>
92 Cyg_Mboxt<T,QUEUE_SIZE>::~Cyg_Mboxt()
93 {
94 CYG_REPORT_FUNCTION();
95 CYG_ASSERT( 0 == count, "Deleting mboxt with messages");
96 CYG_ASSERT( get_threadq.empty(), "Deleting mboxt with threads waiting to get");
97 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
98 CYG_ASSERT( put_threadq.empty(), "Deleting mboxt with threads waiting to put");
99 #endif
100 }
101
102 // -------------------------------------------------------------------------
103 // debugging/assert function
104
105 #ifdef CYGDBG_USE_ASSERTS
106
107 template <class T, cyg_count32 QUEUE_SIZE>
108 cyg_bool
109 Cyg_Mboxt<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal)
110 {
111 CYG_REPORT_FUNCTION();
112
113 if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
114 // then the whole thing is invalid, and we know it.
115 // so return OK, since this check should NOT make an error.
116 return true;
117
118 // check that we have a non-NULL pointer first
119 if( this == NULL ) return false;
120
121 #if 0 // thread queues do not have checking funcs.
122 if ( ! get_threadq.check_this( zeal ) ) return false;
123 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
124 if ( ! put_threadq.check_this( zeal ) ) return false;
125 #endif
126 #endif
127
128 switch( zeal )
129 {
130 case cyg_system_test:
131 case cyg_extreme:
132 case cyg_thorough:
133 case cyg_quick:
134 case cyg_trivial:
135 // plenty of scope for fencepost problems here
136 if ( size < count ) return false;
137 if ( size <= base ) return false;
138 if ( 0 > count ) return false;
139 if ( 0 > base ) return false;
140
141 // there was initially a test of the form
142 // (0 < count && count < size) && ! threadqueue.empty()
143 // here - ie. there should only be people waiting if the Q is full
144 // or empty. This is bogus, anyone else might run between a waiter
145 // being awoken, so there can be a 2nd waiter in the Q and a free
146 // slot (say) simultaneously.
147
148 // Further, we need 2 queues; imagine a 10-slot itemqueue with 25
149 // attempts to put to it, so 15 sleep. 10 other threads get,
150 // awakening 10 of the 15 put-sleepers. Another one gets, and
151 // can't because there is no data there _yet_; it sleeps, and the
152 // 10 awakened threads cycle through the run queue, each putting,
153 // the first awakens the get-sleeper, which in turn awakens a
154 // further put-sleeper.
155
156 // This requirement for 2 queue only holds if Ngetters > 2 * Nslots
157 // or Nputters > 2 * Nslots; if these are both false, one queue
158 // will suffice. This could be an optimisation for the future -
159 // wow, 4 bytes.
160
161 case cyg_none:
162 default:
163 break;
164 };
165
166 return true;
167 }
168
169 #endif
170
171
172 // -------------------------------------------------------------------------
173 // From here downwards, these are the major functions of the template; if
174 // being genuinely used as a template they should probably not be inlined.
175 // If being used to construct a specific class, with explicit functions,
176 // then they should be. This is controlled by:
177
178 #ifdef CYGIMP_MBOXT_INLINE
179 #define CYG_MBOXT_INLINE inline
180 #else
181 #define CYG_MBOXT_INLINE
182 #endif
183
184 // -------------------------------------------------------------------------
185 // Get an item, or wait for one to arrive
186
187 template <class T, cyg_count32 QUEUE_SIZE>
188 CYG_MBOXT_INLINE cyg_bool
189 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem )
190 {
191 CYG_REPORT_FUNCTION();
192 cyg_bool result = true;
193
194 Cyg_Thread *self = Cyg_Thread::self();
195
196 // Prevent preemption
197 Cyg_Scheduler::lock();
198
199 CYG_ASSERTCLASS( this, "Bad this pointer");
200
201 CYG_INSTRUMENT_MBOXT(GET, this, count);
202
203 // Loop while the mboxt is empty, sleeping each time around
204 // the loop. This copes with the possibility of a higher priority
205 // thread grabbing the message between the wakeup in unlock() and
206 // this thread actually starting.
207
208 while( result && (0 == count) ) {
209 self->set_sleep_reason( Cyg_Thread::WAIT );
210 self->sleep();
211 get_threadq.enqueue( self );
212
213 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
214 CYG_ASSERT( 1 == Cyg_Scheduler::get_sched_lock(),
215 "Called with non-zero scheduler lock");
216
217 // Unlock scheduler and allow other threads to run
218 Cyg_Scheduler::unlock();
219 Cyg_Scheduler::lock();
220
221 CYG_ASSERTCLASS( this, "Bad this pointer");
222
223 switch( self->get_wake_reason() )
224 {
225 case Cyg_Thread::DESTRUCT:
226 case Cyg_Thread::BREAK:
227 result = false;
228 break;
229
230 case Cyg_Thread::EXIT:
231 self->exit();
232 break;
233
234 default:
235 break;
236 }
237 }
238
239 if ( result ) {
240 CYG_INSTRUMENT_MBOXT(GOT, this, count);
241
242 ritem = itemqueue[ (count--, base++) ];
243 CYG_ASSERT( 0 <= count, "Count went -ve" );
244 CYG_ASSERT( size >= base, "Base overflow" );
245
246 if ( size <= base )
247 base = 0;
248
249 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
250 wakeup_waiter( put_threadq );
251 #endif
252 }
253
254 // Unlock the scheduler and maybe switch threads
255 Cyg_Scheduler::unlock();
256
257 CYG_ASSERTCLASS( this, "Bad this pointer");
258 CYG_REPORT_RETVAL( result );
259 return result;
260 }
261
262
263 // -------------------------------------------------------------------------
264 // Try to get an item with an absolute timeout and return success.
265
266 #ifdef CYGFUN_KERNEL_THREADS_TIMER
267 template <class T, cyg_count32 QUEUE_SIZE>
268 CYG_MBOXT_INLINE cyg_bool
269 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
270 {
271 CYG_REPORT_FUNCTION();
272 cyg_bool result = true;
273
274 Cyg_Thread *self = Cyg_Thread::self();
275
276 // Prevent preemption
277 Cyg_Scheduler::lock();
278
279 CYG_ASSERTCLASS( this, "Bad this pointer");
280
281 CYG_INSTRUMENT_MBOXT(GET, this, count);
282
283 // Set the timer _once_ outside the loop.
284 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
285
286 // If the timeout is in the past, the wake reason will have been
287 // set to something other than NONE already. Set the result false
288 // to force an immediate return.
289
290 if( self->get_wake_reason() != Cyg_Thread::NONE )
291 result = false;
292
293 // Loop while the mboxt is empty, sleeping each time around the loop.
294 // This copes with the possibility of a higher priority thread grabbing
295 // the message between the wakeup in put()&c and this thread actually
296 // starting.
297 while ( result && (0 == count) ) {
298 // must reset the sleep reason every time
299 self->set_sleep_reason( Cyg_Thread::TIMEOUT );
300 self->sleep();
301 get_threadq.enqueue( self );
302
303 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
304 CYG_ASSERT( 1 == Cyg_Scheduler::get_sched_lock(),
305 "Called with non-zero scheduler lock");
306
307 // Unlock scheduler and allow other threads to run
308 Cyg_Scheduler::unlock();
309 Cyg_Scheduler::lock();
310
311 CYG_ASSERTCLASS( this, "Bad this pointer");
312
313 switch( self->get_wake_reason() )
314 {
315 case Cyg_Thread::TIMEOUT:
316 result = false;
317 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
318 break;
319
320 case Cyg_Thread::DESTRUCT:
321 case Cyg_Thread::BREAK:
322 result = false;
323 break;
324
325 case Cyg_Thread::EXIT:
326 self->exit();
327 break;
328
329 default:
330 break;
331 }
332 }
333
334 // clear the timer; if it actually fired, no worries.
335 self->clear_timer();
336
337 if ( result ) {
338
339 CYG_INSTRUMENT_MBOXT(GOT, this, count);
340
341 ritem = itemqueue[ (count--, base++) ];
342 CYG_ASSERT( 0 <= count, "Count went -ve" );
343 CYG_ASSERT( size >= base, "Base overflow" );
344
345 if ( size <= base )
346 base = 0;
347
348 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
349 wakeup_waiter( put_threadq );
350 #endif
351 }
352
353 // Unlock the scheduler and maybe switch threads
354 Cyg_Scheduler::unlock();
355
356 CYG_ASSERTCLASS( this, "Bad this pointer");
357 CYG_REPORT_RETVAL( result );
358 return result;
359 }
360 #endif // CYGFUN_KERNEL_THREADS_TIMER
361
362 // -------------------------------------------------------------------------
363 // Try to get an item and return success.
364
365 template <class T, cyg_count32 QUEUE_SIZE>
366 CYG_MBOXT_INLINE cyg_bool
367 Cyg_Mboxt<T,QUEUE_SIZE>::tryget( T &ritem )
368 {
369 CYG_REPORT_FUNCTION();
370
371 CYG_ASSERTCLASS( this, "Bad this pointer");
372
373 // Prevent preemption
374 Cyg_Scheduler::lock();
375
376 CYG_INSTRUMENT_MBOXT(TRY, this, count);
377
378 cyg_bool result = ( 0 < count );
379 // If the mboxt is not empty, grab an item and return it.
380 if ( result ) {
381 ritem = itemqueue[ (count--, base++) ];
382 CYG_ASSERT( 0 <= count, "Count went -ve" );
383 CYG_ASSERT( size >= base, "Base overflow" );
384 if ( size <= base )
385 base = 0;
386
387 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
388 wakeup_waiter( put_threadq );
389 #endif
390 }
391
392 // Unlock the scheduler and maybe switch threads
393 Cyg_Scheduler::unlock();
394
395 return result;
396 }
397
398 // -------------------------------------------------------------------------
399 // get next item without removing it
400 template <class T, cyg_count32 QUEUE_SIZE>
401 CYG_MBOXT_INLINE cyg_bool
402 Cyg_Mboxt<T,QUEUE_SIZE>::peek_item( T &ritem )
403 {
404 CYG_REPORT_FUNCTION();
405
406 CYG_ASSERTCLASS( this, "Bad this pointer");
407
408 // Prevent preemption
409 Cyg_Scheduler::lock();
410
411 CYG_INSTRUMENT_MBOXT(TRY, this, count);
412
413 cyg_bool result = ( 0 < count );
414 // If the mboxt is not empty, grab an item and return it.
415 if ( result )
416 ritem = itemqueue[ base ];
417
418 // Unlock the scheduler and maybe switch threads
419 Cyg_Scheduler::unlock();
420
421 return result;
422 }
423
424 // -------------------------------------------------------------------------
425 // Put an item in the queue; wait if full.
426
427 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
428 template <class T, cyg_count32 QUEUE_SIZE>
429 CYG_MBOXT_INLINE cyg_bool
430 Cyg_Mboxt<T,QUEUE_SIZE>::put( const T item )
431 {
432 CYG_REPORT_FUNCTION();
433 cyg_bool result = true;
434
435 Cyg_Thread *self = Cyg_Thread::self();
436
437 // Prevent preemption
438 Cyg_Scheduler::lock();
439
440 CYG_INSTRUMENT_MBOXT(PUT, this, count);
441 CYG_ASSERTCLASS( this, "Bad this pointer");
442
443 while ( result && (size == count) ) {
444 self->set_sleep_reason( Cyg_Thread::WAIT );
445 self->sleep();
446 put_threadq.enqueue( self );
447
448 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
449 CYG_ASSERT( 1 == Cyg_Scheduler::get_sched_lock(),
450 "Called with non-zero scheduler lock");
451
452 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
453 Cyg_Scheduler::lock();
454
455 CYG_ASSERTCLASS( this, "Bad this pointer");
456
457 switch( self->get_wake_reason() )
458 {
459 case Cyg_Thread::DESTRUCT:
460 case Cyg_Thread::BREAK:
461 result = false;
462 break;
463
464 case Cyg_Thread::EXIT:
465 self->exit();
466 break;
467
468 default:
469 break;
470 }
471 }
472
473 if ( result ) {
474 cyg_count32 in = base + (count++);
475 if ( size <= in )
476 in -= size;
477
478 CYG_ASSERT( size > in, "in overflow" );
479 CYG_ASSERT( 0 <= in, "in overflow" );
480 CYG_ASSERT( size >= count, "count overflow" );
481
482 itemqueue[ in ] = item;
483
484 wakeup_waiter( get_threadq );
485 }
486 CYG_ASSERTCLASS( this, "Bad this pointer");
487
488 // Unlock the scheduler and maybe switch threads
489 Cyg_Scheduler::unlock();
490 CYG_REPORT_RETVAL( result );
491 return result;
492 }
493
494 // -------------------------------------------------------------------------
495 // Put an item in the queue; wait if full, with an absolute timeout;
496 // return success.
497
498 #ifdef CYGFUN_KERNEL_THREADS_TIMER
499 template <class T, cyg_count32 QUEUE_SIZE>
500 CYG_MBOXT_INLINE cyg_bool
501 Cyg_Mboxt<T,QUEUE_SIZE>::put( const T item, cyg_tick_count abs_timeout )
502 {
503 CYG_REPORT_FUNCTION();
504 cyg_bool result = true;
505
506 Cyg_Thread *self = Cyg_Thread::self();
507
508 // Prevent preemption
509 Cyg_Scheduler::lock();
510
511 CYG_INSTRUMENT_MBOXT(PUT, this, count);
512 CYG_ASSERTCLASS( this, "Bad this pointer");
513
514 // Set the timer _once_ outside the loop.
515 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
516
517 // If the timeout is in the past, the wake reason will have been
518 // set to something other than NONE already. Set the result false
519 // to force an immediate return.
520
521 if( self->get_wake_reason() != Cyg_Thread::NONE )
522 result = false;
523
524 // Loop while the mboxt is full, sleeping each time around the loop.
525 // This copes with the possibility of a higher priority thread filling
526 // the empty slot between the wakeup in get()&c and this thread
527 // actually starting.
528 while ( result && (size == count) ) {
529 // must reset the sleep reason every time
530 self->set_sleep_reason( Cyg_Thread::TIMEOUT );
531 self->sleep();
532 put_threadq.enqueue( self );
533
534 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
535 CYG_ASSERT( 1 == Cyg_Scheduler::get_sched_lock(),
536 "Called with non-zero scheduler lock");
537
538 // Unlock scheduler and allow other threads to run
539 Cyg_Scheduler::unlock();
540 Cyg_Scheduler::lock();
541
542 CYG_ASSERTCLASS( this, "Bad this pointer");
543
544 switch( self->get_wake_reason() )
545 {
546 case Cyg_Thread::TIMEOUT:
547 result = false;
548 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
549 break;
550
551 case Cyg_Thread::DESTRUCT:
552 case Cyg_Thread::BREAK:
553 result = false;
554 break;
555
556 case Cyg_Thread::EXIT:
557 self->exit();
558 break;
559
560 default:
561 break;
562 }
563 }
564
565 // clear the timer; if it actually fired, no worries.
566 self->clear_timer();
567
568 if ( result ) {
569 cyg_count32 in = base + (count++);
570 if ( size <= in )
571 in -= size;
572
573 CYG_ASSERT( size > in, "in overflow" );
574 CYG_ASSERT( 0 <= in, "in overflow" );
575 CYG_ASSERT( size >= count, "count overflow" );
576
577 itemqueue[ in ] = item;
578
579 wakeup_waiter( get_threadq );
580 }
581 CYG_ASSERTCLASS( this, "Bad this pointer");
582
583 // Unlock the scheduler and maybe switch threads
584 Cyg_Scheduler::unlock();
585 CYG_REPORT_RETVAL( result );
586 return result;
587 }
588 #endif // CYGFUN_KERNEL_THREADS_TIMER
589 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
590
591 // -------------------------------------------------------------------------
592 // Try to put an item in the queue and return success; queue may be full.
593
594 template <class T, cyg_count32 QUEUE_SIZE>
595 CYG_MBOXT_INLINE cyg_bool
596 Cyg_Mboxt<T,QUEUE_SIZE>::tryput( const T item )
597 {
598 CYG_REPORT_FUNCTION();
599
600 // Prevent preemption
601 Cyg_Scheduler::lock();
602
603 CYG_INSTRUMENT_MBOXT(PUT, this, count);
604 CYG_ASSERTCLASS( this, "Bad this pointer");
605
606 if ( size == count ) {
607 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
608 return false; // the mboxt is full
609 }
610
611 cyg_count32 in = base + (count++);
612 if ( size <= in )
613 in -= size;
614
615 CYG_ASSERT( size > in, "in overflow" );
616 CYG_ASSERT( 0 <= in, "in overflow" );
617 CYG_ASSERT( size >= count, "count overflow" );
618
619 itemqueue[ in ] = item;
620
621 CYG_ASSERTCLASS( this, "Bad this pointer");
622
623 wakeup_waiter( get_threadq );
624
625 // Unlock the scheduler and maybe switch threads
626 Cyg_Scheduler::unlock();
627
628 return true;
629 }
630
631
632 // -------------------------------------------------------------------------
633 #endif // ifndef CYGONCE_KERNEL_MBOXT_INL
634 // EOF mboxt.inl