1 #ifndef CYGONCE_KERNEL_MBOXT_INL
2 #define CYGONCE_KERNEL_MBOXT_INL
3 //==========================================================================
7 // Mboxt mbox template class implementation
9 //==========================================================================
10 //####ECOSGPLCOPYRIGHTBEGIN####
11 // -------------------------------------------
12 // This file is part of eCos, the Embedded Configurable Operating System.
13 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
15 // eCos is free software; you can redistribute it and/or modify it under
16 // the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 or (at your option) any later version.
19 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 // You should have received a copy of the GNU General Public License along
25 // with eCos; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 // As a special exception, if other files instantiate templates or use macros
29 // or inline functions from this file, or you compile this file and link it
30 // with other works to produce a work based on this file, this file does not
31 // by itself cause the resulting work to be covered by the GNU General Public
32 // License. However the source code for this file must still be made available
33 // in accordance with section (3) of the GNU General Public License.
35 // This exception does not invalidate any other reasons why a work based on
36 // this file might be covered by the GNU General Public License.
38 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 // at http://sources.redhat.com/ecos/ecos-license/
40 // -------------------------------------------
41 //####ECOSGPLCOPYRIGHTEND####
42 //==========================================================================
43 //#####DESCRIPTIONBEGIN####
48 // Purpose: Mboxt template implementation
49 // Description: This file contains the implementations of the mboxt
52 //####DESCRIPTIONEND####
54 //==========================================================================
56 #include <pkgconf/kernel.h>
58 #include <cyg/kernel/ktypes.h> // base kernel types
59 #include <cyg/infra/cyg_trac.h> // tracing macros
60 #include <cyg/infra/cyg_ass.h> // assertion macros
61 #include <cyg/kernel/instrmnt.h> // instrumentation
63 #include <cyg/kernel/mboxt.hxx> // our header
65 #include <cyg/kernel/thread.inl> // thread inlines
66 #include <cyg/kernel/sched.inl> // scheduler inlines
67 #include <cyg/kernel/clock.inl> // clock inlines
69 // -------------------------------------------------------------------------
70 // inline function for awakening waiting threads
72 template <class T, cyg_count32 QUEUE_SIZE>
74 Cyg_Mboxt<T,QUEUE_SIZE>::wakeup_waiter( Cyg_ThreadQueue &q )
77 // The queue is non-empty, so grab the next thread and wake it up.
78 Cyg_Thread *thread = q.dequeue();
80 CYG_ASSERTCLASS( thread, "Bad thread pointer");
82 thread->set_wake_reason( Cyg_Thread::DONE );
84 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
88 // -------------------------------------------------------------------------
91 template <class T, cyg_count32 QUEUE_SIZE>
92 Cyg_Mboxt<T,QUEUE_SIZE>::Cyg_Mboxt()
94 CYG_REPORT_FUNCTION();
99 // -------------------------------------------------------------------------
102 template <class T, cyg_count32 QUEUE_SIZE>
103 Cyg_Mboxt<T,QUEUE_SIZE>::~Cyg_Mboxt()
105 CYG_REPORT_FUNCTION();
106 CYG_ASSERT( 0 == count, "Deleting mboxt with messages");
107 CYG_ASSERT( get_threadq.empty(), "Deleting mboxt with threads waiting to get");
108 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
109 CYG_ASSERT( put_threadq.empty(), "Deleting mboxt with threads waiting to put");
113 // -------------------------------------------------------------------------
114 // debugging/assert function
116 #ifdef CYGDBG_USE_ASSERTS
118 template <class T, cyg_count32 QUEUE_SIZE>
120 Cyg_Mboxt<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
122 CYG_REPORT_FUNCTION();
124 if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
125 // then the whole thing is invalid, and we know it.
126 // so return OK, since this check should NOT make an error.
129 // check that we have a non-NULL pointer first
130 if( this == NULL ) return false;
132 #if 0 // thread queues do not have checking funcs.
133 if ( ! get_threadq.check_this( zeal ) ) return false;
134 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
135 if ( ! put_threadq.check_this( zeal ) ) return false;
141 case cyg_system_test:
146 // plenty of scope for fencepost problems here
147 if ( size < count ) return false;
148 if ( size <= base ) return false;
149 if ( 0 > count ) return false;
150 if ( 0 > base ) return false;
152 // there was initially a test of the form
153 // (0 < count && count < size) && ! threadqueue.empty()
154 // here - ie. there should only be people waiting if the Q is full
155 // or empty. This is bogus, anyone else might run between a waiter
156 // being awoken, so there can be a 2nd waiter in the Q and a free
157 // slot (say) simultaneously.
159 // Further, we need 2 queues; imagine a 10-slot itemqueue with 25
160 // attempts to put to it, so 15 sleep. 10 other threads get,
161 // awakening 10 of the 15 put-sleepers. Another one gets, and
162 // can't because there is no data there _yet_; it sleeps, and the
163 // 10 awakened threads cycle through the run queue, each putting,
164 // the first awakens the get-sleeper, which in turn awakens a
165 // further put-sleeper.
167 // This requirement for 2 queue only holds if Ngetters > 2 * Nslots
168 // or Nputters > 2 * Nslots; if these are both false, one queue
169 // will suffice. This could be an optimisation for the future -
183 // -------------------------------------------------------------------------
184 // From here downwards, these are the major functions of the template; if
185 // being genuinely used as a template they should probably not be inlined.
186 // If being used to construct a specific class, with explicit functions,
187 // then they should be. This is controlled by:
189 #ifdef CYGIMP_MBOXT_INLINE
190 #define CYG_MBOXT_INLINE inline
192 #define CYG_MBOXT_INLINE
195 // -------------------------------------------------------------------------
196 // Get an item, or wait for one to arrive
198 template <class T, cyg_count32 QUEUE_SIZE>
199 CYG_MBOXT_INLINE cyg_bool
200 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem )
202 CYG_REPORT_FUNCTION();
203 cyg_bool result = true;
205 Cyg_Thread *self = Cyg_Thread::self();
207 // Prevent preemption
208 Cyg_Scheduler::lock();
210 CYG_ASSERTCLASS( this, "Bad this pointer");
212 CYG_INSTRUMENT_MBOXT(GET, this, count);
214 // Loop while the mboxt is empty, sleeping each time around
215 // the loop. This copes with the possibility of a higher priority
216 // thread grabbing the message between the wakeup in unlock() and
217 // this thread actually starting.
219 while( result && (0 == count) ) {
220 self->set_sleep_reason( Cyg_Thread::WAIT );
222 get_threadq.enqueue( self );
224 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
226 // Allow other threads to run
227 Cyg_Scheduler::reschedule();
229 CYG_ASSERTCLASS( this, "Bad this pointer");
231 switch( self->get_wake_reason() )
233 case Cyg_Thread::DESTRUCT:
234 case Cyg_Thread::BREAK:
238 case Cyg_Thread::EXIT:
248 CYG_INSTRUMENT_MBOXT(GOT, this, count);
250 ritem = itemqueue[ (count--, base++) ];
251 CYG_ASSERT( 0 <= count, "Count went -ve" );
252 CYG_ASSERT( size >= base, "Base overflow" );
257 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
258 wakeup_waiter( put_threadq );
262 // Unlock the scheduler and maybe switch threads
263 Cyg_Scheduler::unlock();
265 CYG_ASSERTCLASS( this, "Bad this pointer");
266 CYG_REPORT_RETVAL( result );
271 // -------------------------------------------------------------------------
272 // Try to get an item with an absolute timeout and return success.
274 #ifdef CYGFUN_KERNEL_THREADS_TIMER
275 template <class T, cyg_count32 QUEUE_SIZE>
276 CYG_MBOXT_INLINE cyg_bool
277 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
279 CYG_REPORT_FUNCTION();
280 cyg_bool result = true;
282 Cyg_Thread *self = Cyg_Thread::self();
284 // Prevent preemption
285 Cyg_Scheduler::lock();
287 CYG_ASSERTCLASS( this, "Bad this pointer");
289 CYG_INSTRUMENT_MBOXT(GET, this, count);
291 // Set the timer _once_ outside the loop.
292 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
294 // If the timeout is in the past, the wake reason will have been
295 // set to something other than NONE already. Set the result false
296 // to force an immediate return.
298 if( self->get_wake_reason() != Cyg_Thread::NONE )
301 // Loop while the mboxt is empty, sleeping each time around the loop.
302 // This copes with the possibility of a higher priority thread grabbing
303 // the message between the wakeup in put()&c and this thread actually
305 while ( result && (0 == count) ) {
306 // must reset the sleep reason every time
307 self->set_sleep_reason( Cyg_Thread::TIMEOUT );
309 get_threadq.enqueue( self );
311 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
313 // Allow other threads to run
314 Cyg_Scheduler::reschedule();
316 CYG_ASSERTCLASS( this, "Bad this pointer");
318 switch( self->get_wake_reason() )
320 case Cyg_Thread::TIMEOUT:
322 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
325 case Cyg_Thread::DESTRUCT:
326 case Cyg_Thread::BREAK:
330 case Cyg_Thread::EXIT:
339 // clear the timer; if it actually fired, no worries.
344 CYG_INSTRUMENT_MBOXT(GOT, this, count);
346 ritem = itemqueue[ (count--, base++) ];
347 CYG_ASSERT( 0 <= count, "Count went -ve" );
348 CYG_ASSERT( size >= base, "Base overflow" );
353 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
354 wakeup_waiter( put_threadq );
358 // Unlock the scheduler and maybe switch threads
359 Cyg_Scheduler::unlock();
361 CYG_ASSERTCLASS( this, "Bad this pointer");
362 CYG_REPORT_RETVAL( result );
365 #endif // CYGFUN_KERNEL_THREADS_TIMER
367 // -------------------------------------------------------------------------
368 // Try to get an item and return success.
370 template <class T, cyg_count32 QUEUE_SIZE>
371 CYG_MBOXT_INLINE cyg_bool
372 Cyg_Mboxt<T,QUEUE_SIZE>::tryget( T &ritem )
374 CYG_REPORT_FUNCTION();
376 CYG_ASSERTCLASS( this, "Bad this pointer");
378 // Prevent preemption
379 Cyg_Scheduler::lock();
381 CYG_INSTRUMENT_MBOXT(TRY, this, count);
383 cyg_bool result = ( 0 < count );
384 // If the mboxt is not empty, grab an item and return it.
386 ritem = itemqueue[ (count--, base++) ];
387 CYG_ASSERT( 0 <= count, "Count went -ve" );
388 CYG_ASSERT( size >= base, "Base overflow" );
392 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
393 wakeup_waiter( put_threadq );
397 // Unlock the scheduler and maybe switch threads
398 Cyg_Scheduler::unlock();
403 // -------------------------------------------------------------------------
404 // get next item without removing it
405 template <class T, cyg_count32 QUEUE_SIZE>
406 CYG_MBOXT_INLINE cyg_bool
407 Cyg_Mboxt<T,QUEUE_SIZE>::peek_item( T &ritem )
409 CYG_REPORT_FUNCTION();
411 CYG_ASSERTCLASS( this, "Bad this pointer");
413 // Prevent preemption
414 Cyg_Scheduler::lock();
416 CYG_INSTRUMENT_MBOXT(TRY, this, count);
418 cyg_bool result = ( 0 < count );
419 // If the mboxt is not empty, grab an item and return it.
421 ritem = itemqueue[ base ];
423 // Unlock the scheduler and maybe switch threads
424 Cyg_Scheduler::unlock();
429 // -------------------------------------------------------------------------
430 // Put an item in the queue; wait if full.
432 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
433 template <class T, cyg_count32 QUEUE_SIZE>
434 CYG_MBOXT_INLINE cyg_bool
435 Cyg_Mboxt<T,QUEUE_SIZE>::put( const T item )
437 CYG_REPORT_FUNCTION();
438 cyg_bool result = true;
440 Cyg_Thread *self = Cyg_Thread::self();
442 // Prevent preemption
443 Cyg_Scheduler::lock();
445 CYG_INSTRUMENT_MBOXT(PUT, this, count);
446 CYG_ASSERTCLASS( this, "Bad this pointer");
448 while ( result && (size == count) ) {
449 self->set_sleep_reason( Cyg_Thread::WAIT );
451 put_threadq.enqueue( self );
453 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
455 // Allow other threads to run
456 Cyg_Scheduler::reschedule();
458 CYG_ASSERTCLASS( this, "Bad this pointer");
460 switch( self->get_wake_reason() )
462 case Cyg_Thread::DESTRUCT:
463 case Cyg_Thread::BREAK:
467 case Cyg_Thread::EXIT:
477 cyg_count32 in = base + (count++);
481 CYG_ASSERT( size > in, "in overflow" );
482 CYG_ASSERT( 0 <= in, "in overflow" );
483 CYG_ASSERT( size >= count, "count overflow" );
485 itemqueue[ in ] = item;
487 wakeup_waiter( get_threadq );
489 CYG_ASSERTCLASS( this, "Bad this pointer");
491 // Unlock the scheduler and maybe switch threads
492 Cyg_Scheduler::unlock();
493 CYG_REPORT_RETVAL( result );
497 // -------------------------------------------------------------------------
498 // Put an item in the queue; wait if full, with an absolute timeout;
501 #ifdef CYGFUN_KERNEL_THREADS_TIMER
502 template <class T, cyg_count32 QUEUE_SIZE>
503 CYG_MBOXT_INLINE cyg_bool
504 Cyg_Mboxt<T,QUEUE_SIZE>::put( const T item, cyg_tick_count abs_timeout )
506 CYG_REPORT_FUNCTION();
507 cyg_bool result = true;
509 Cyg_Thread *self = Cyg_Thread::self();
511 // Prevent preemption
512 Cyg_Scheduler::lock();
514 CYG_INSTRUMENT_MBOXT(PUT, this, count);
515 CYG_ASSERTCLASS( this, "Bad this pointer");
517 // Set the timer _once_ outside the loop.
518 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
520 // If the timeout is in the past, the wake reason will have been
521 // set to something other than NONE already. Set the result false
522 // to force an immediate return.
524 if( self->get_wake_reason() != Cyg_Thread::NONE )
527 // Loop while the mboxt is full, sleeping each time around the loop.
528 // This copes with the possibility of a higher priority thread filling
529 // the empty slot between the wakeup in get()&c and this thread
530 // actually starting.
531 while ( result && (size == count) ) {
532 // must reset the sleep reason every time
533 self->set_sleep_reason( Cyg_Thread::TIMEOUT );
535 put_threadq.enqueue( self );
537 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
539 // Allow other threads to run
540 Cyg_Scheduler::reschedule();
542 CYG_ASSERTCLASS( this, "Bad this pointer");
544 switch( self->get_wake_reason() )
546 case Cyg_Thread::TIMEOUT:
548 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
551 case Cyg_Thread::DESTRUCT:
552 case Cyg_Thread::BREAK:
556 case Cyg_Thread::EXIT:
565 // clear the timer; if it actually fired, no worries.
569 cyg_count32 in = base + (count++);
573 CYG_ASSERT( size > in, "in overflow" );
574 CYG_ASSERT( 0 <= in, "in overflow" );
575 CYG_ASSERT( size >= count, "count overflow" );
577 itemqueue[ in ] = item;
579 wakeup_waiter( get_threadq );
581 CYG_ASSERTCLASS( this, "Bad this pointer");
583 // Unlock the scheduler and maybe switch threads
584 Cyg_Scheduler::unlock();
585 CYG_REPORT_RETVAL( result );
588 #endif // CYGFUN_KERNEL_THREADS_TIMER
589 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
591 // -------------------------------------------------------------------------
592 // Try to put an item in the queue and return success; queue may be full.
594 template <class T, cyg_count32 QUEUE_SIZE>
595 CYG_MBOXT_INLINE cyg_bool
596 Cyg_Mboxt<T,QUEUE_SIZE>::tryput( const T item )
598 CYG_REPORT_FUNCTION();
600 // Prevent preemption
601 Cyg_Scheduler::lock();
603 CYG_INSTRUMENT_MBOXT(PUT, this, count);
604 CYG_ASSERTCLASS( this, "Bad this pointer");
606 if ( size == count ) {
607 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
608 return false; // the mboxt is full
611 cyg_count32 in = base + (count++);
615 CYG_ASSERT( size > in, "in overflow" );
616 CYG_ASSERT( 0 <= in, "in overflow" );
617 CYG_ASSERT( size >= count, "count overflow" );
619 itemqueue[ in ] = item;
621 CYG_ASSERTCLASS( this, "Bad this pointer");
623 wakeup_waiter( get_threadq );
625 // Unlock the scheduler and maybe switch threads
626 Cyg_Scheduler::unlock();
632 // -------------------------------------------------------------------------
633 #endif // ifndef CYGONCE_KERNEL_MBOXT_INL