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.
14 // Copyright (C) 2006 eCosCentric Ltd.
16 // eCos is free software; you can redistribute it and/or modify it under
17 // the terms of the GNU General Public License as published by the Free
18 // Software Foundation; either version 2 or (at your option) any later version.
20 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
21 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 // You should have received a copy of the GNU General Public License along
26 // with eCos; if not, write to the Free Software Foundation, Inc.,
27 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
29 // As a special exception, if other files instantiate templates or use macros
30 // or inline functions from this file, or you compile this file and link it
31 // with other works to produce a work based on this file, this file does not
32 // by itself cause the resulting work to be covered by the GNU General Public
33 // License. However the source code for this file must still be made available
34 // in accordance with section (3) of the GNU General Public License.
36 // This exception does not invalidate any other reasons why a work based on
37 // this file might be covered by the GNU General Public License.
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
46 // Purpose: Mboxt template implementation
47 // Description: This file contains the implementations of the mboxt
50 //####DESCRIPTIONEND####
52 //==========================================================================
54 #include <pkgconf/kernel.h>
56 #include <cyg/kernel/ktypes.h> // base kernel types
57 #include <cyg/infra/cyg_trac.h> // tracing macros
58 #include <cyg/infra/cyg_ass.h> // assertion macros
59 #include <cyg/kernel/instrmnt.h> // instrumentation
61 #include <cyg/kernel/mboxt.hxx> // our header
63 #include <cyg/kernel/thread.inl> // thread inlines
64 #include <cyg/kernel/sched.inl> // scheduler inlines
65 #include <cyg/kernel/clock.inl> // clock inlines
67 // -------------------------------------------------------------------------
68 // inline function for awakening waiting threads
70 template <class T, cyg_count32 QUEUE_SIZE>
72 Cyg_Mboxt<T,QUEUE_SIZE>::wakeup_waiter( Cyg_ThreadQueue &q )
75 // The queue is non-empty, so grab the next thread and wake it up.
76 Cyg_Thread *thread = q.dequeue();
78 CYG_ASSERTCLASS( thread, "Bad thread pointer");
80 thread->set_wake_reason( Cyg_Thread::DONE );
82 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
86 // -------------------------------------------------------------------------
89 template <class T, cyg_count32 QUEUE_SIZE>
90 Cyg_Mboxt<T,QUEUE_SIZE>::Cyg_Mboxt()
92 CYG_REPORT_FUNCTION();
97 // -------------------------------------------------------------------------
100 template <class T, cyg_count32 QUEUE_SIZE>
101 Cyg_Mboxt<T,QUEUE_SIZE>::~Cyg_Mboxt()
103 CYG_REPORT_FUNCTION();
104 CYG_ASSERT( 0 == count, "Deleting mboxt with messages");
105 CYG_ASSERT( get_threadq.empty(), "Deleting mboxt with threads waiting to get");
106 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
107 CYG_ASSERT( put_threadq.empty(), "Deleting mboxt with threads waiting to put");
111 // -------------------------------------------------------------------------
112 // debugging/assert function
114 #ifdef CYGDBG_USE_ASSERTS
116 template <class T, cyg_count32 QUEUE_SIZE>
118 Cyg_Mboxt<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
120 CYG_REPORT_FUNCTION();
122 if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
123 // then the whole thing is invalid, and we know it.
124 // so return OK, since this check should NOT make an error.
127 // check that we have a non-NULL pointer first
128 if( this == NULL ) return false;
130 #if 0 // thread queues do not have checking funcs.
131 if ( ! get_threadq.check_this( zeal ) ) return false;
132 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
133 if ( ! put_threadq.check_this( zeal ) ) return false;
139 case cyg_system_test:
144 // plenty of scope for fencepost problems here
145 if ( size < count ) return false;
146 if ( size <= base ) return false;
147 if ( 0 > count ) return false;
148 if ( 0 > base ) return false;
150 // there was initially a test of the form
151 // (0 < count && count < size) && ! threadqueue.empty()
152 // here - ie. there should only be people waiting if the Q is full
153 // or empty. This is bogus, anyone else might run between a waiter
154 // being awoken, so there can be a 2nd waiter in the Q and a free
155 // slot (say) simultaneously.
157 // Further, we need 2 queues; imagine a 10-slot itemqueue with 25
158 // attempts to put to it, so 15 sleep. 10 other threads get,
159 // awakening 10 of the 15 put-sleepers. Another one gets, and
160 // can't because there is no data there _yet_; it sleeps, and the
161 // 10 awakened threads cycle through the run queue, each putting,
162 // the first awakens the get-sleeper, which in turn awakens a
163 // further put-sleeper.
165 // This requirement for 2 queue only holds if Ngetters > 2 * Nslots
166 // or Nputters > 2 * Nslots; if these are both false, one queue
167 // will suffice. This could be an optimisation for the future -
181 // -------------------------------------------------------------------------
182 // From here downwards, these are the major functions of the template; if
183 // being genuinely used as a template they should probably not be inlined.
184 // If being used to construct a specific class, with explicit functions,
185 // then they should be. This is controlled by:
187 #ifdef CYGIMP_MBOXT_INLINE
188 #define CYG_MBOXT_INLINE inline
190 #define CYG_MBOXT_INLINE
193 // -------------------------------------------------------------------------
194 // Get an item, or wait for one to arrive
196 template <class T, cyg_count32 QUEUE_SIZE>
197 CYG_MBOXT_INLINE cyg_bool
198 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem )
200 CYG_REPORT_FUNCTION();
201 cyg_bool result = true;
203 Cyg_Thread *self = Cyg_Thread::self();
205 // Prevent preemption
206 Cyg_Scheduler::lock();
208 CYG_ASSERTCLASS( this, "Bad this pointer");
210 CYG_INSTRUMENT_MBOXT(GET, this, count);
212 // Loop while the mboxt is empty, sleeping each time around
213 // the loop. This copes with the possibility of a higher priority
214 // thread grabbing the message between the wakeup in unlock() and
215 // this thread actually starting.
217 while( result && (0 == count) ) {
218 self->set_sleep_reason( Cyg_Thread::WAIT );
220 get_threadq.enqueue( self );
222 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
224 CYG_ASSERTCLASS( this, "Bad this pointer");
226 // Allow other threads to run
227 Cyg_Scheduler::reschedule();
229 switch( self->get_wake_reason() )
231 case Cyg_Thread::DESTRUCT:
232 case Cyg_Thread::BREAK:
236 case Cyg_Thread::EXIT:
246 CYG_INSTRUMENT_MBOXT(GOT, this, count);
248 ritem = itemqueue[ (count--, base++) ];
249 CYG_ASSERT( 0 <= count, "Count went -ve" );
250 CYG_ASSERT( size >= base, "Base overflow" );
255 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
256 wakeup_waiter( put_threadq );
260 CYG_ASSERTCLASS( this, "Bad this pointer");
262 // Unlock the scheduler and maybe switch threads
263 Cyg_Scheduler::unlock();
265 CYG_REPORT_RETVAL( result );
270 // -------------------------------------------------------------------------
271 // Try to get an item with an absolute timeout and return success.
273 #ifdef CYGFUN_KERNEL_THREADS_TIMER
274 template <class T, cyg_count32 QUEUE_SIZE>
275 CYG_MBOXT_INLINE cyg_bool
276 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
278 CYG_REPORT_FUNCTION();
279 cyg_bool result = true;
281 Cyg_Thread *self = Cyg_Thread::self();
283 // Prevent preemption
284 Cyg_Scheduler::lock();
286 CYG_ASSERTCLASS( this, "Bad this pointer");
288 CYG_INSTRUMENT_MBOXT(GET, this, count);
290 // Set the timer _once_ outside the loop.
291 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
293 // If the timeout is in the past, the wake reason will have been
294 // set to something other than NONE already. Set the result false
295 // to force an immediate return.
297 if( self->get_wake_reason() != Cyg_Thread::NONE )
300 // Loop while the mboxt is empty, sleeping each time around the loop.
301 // This copes with the possibility of a higher priority thread grabbing
302 // the message between the wakeup in put()&c and this thread actually
304 while ( result && (0 == count) ) {
305 // must reset the sleep reason every time
306 self->set_sleep_reason( Cyg_Thread::TIMEOUT );
308 get_threadq.enqueue( self );
310 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
312 // Allow other threads to run
313 Cyg_Scheduler::reschedule();
315 CYG_ASSERTCLASS( this, "Bad this pointer");
317 switch( self->get_wake_reason() )
319 case Cyg_Thread::TIMEOUT:
321 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
324 case Cyg_Thread::DESTRUCT:
325 case Cyg_Thread::BREAK:
329 case Cyg_Thread::EXIT:
338 // clear the timer; if it actually fired, no worries.
343 CYG_INSTRUMENT_MBOXT(GOT, this, count);
345 ritem = itemqueue[ (count--, base++) ];
346 CYG_ASSERT( 0 <= count, "Count went -ve" );
347 CYG_ASSERT( size >= base, "Base overflow" );
352 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
353 wakeup_waiter( put_threadq );
357 CYG_ASSERTCLASS( this, "Bad this pointer");
359 // Unlock the scheduler and maybe switch threads
360 Cyg_Scheduler::unlock();
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 // Prevent preemption
377 Cyg_Scheduler::lock();
379 CYG_ASSERTCLASS( this, "Bad this pointer");
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 // Prevent preemption
412 Cyg_Scheduler::lock();
414 CYG_ASSERTCLASS( this, "Bad this pointer");
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