1 #ifndef CYGONCE_KERNEL_MBOXT2_INL
2 #define CYGONCE_KERNEL_MBOXT2_INL
3 //==========================================================================
7 // Mboxt2 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: Mboxt2 template implementation
47 // Description: This file contains the implementations of the mboxt2
50 //####DESCRIPTIONEND####
52 //==========================================================================
54 #include <cyg/kernel/ktypes.h> // base kernel types
55 #include <cyg/infra/cyg_trac.h> // tracing macros
56 #include <cyg/infra/cyg_ass.h> // assertion macros
57 #include <cyg/kernel/instrmnt.h> // instrumentation
59 #include <cyg/kernel/mboxt2.hxx> // our header
61 #include <cyg/kernel/thread.inl> // thread inlines
62 #include <cyg/kernel/sched.inl> // scheduler inlines
63 #include <cyg/kernel/clock.inl> // clock inlines
65 // -------------------------------------------------------------------------
66 // inline function for awakening waiting threads
68 template <class T, cyg_count32 QUEUE_SIZE>
70 Cyg_Mboxt2<T,QUEUE_SIZE>::wakeup_winner( const T &msg )
72 CYG_ASSERT( !get_threadq.empty(), "Where did the winner go?" );
74 // The queue is non-empty, so grab the next thread and wake it up.
75 Cyg_Thread *thread = get_threadq.dequeue();
77 CYG_ASSERTCLASS( thread, "Bad thread pointer");
79 T *msg_ret = (T *)(thread->get_wait_info());
82 thread->set_wake_reason( Cyg_Thread::DONE );
85 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
88 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
89 template <class T, cyg_count32 QUEUE_SIZE>
91 Cyg_Mboxt2<T,QUEUE_SIZE>::wakeup_putter( void )
93 if( !put_threadq.empty() ) {
94 // The queue is non-empty, so grab the next thread and wake it up.
95 Cyg_Thread *thread = put_threadq.dequeue();
97 CYG_ASSERTCLASS( thread, "Bad thread pointer");
99 T *new_msg = (T *)(thread->get_wait_info());
101 cyg_count32 in = base + (count++);
105 CYG_ASSERT( size > in, "in overflow" );
106 CYG_ASSERT( 0 <= in, "in overflow" );
107 CYG_ASSERT( size >= count, "count overflow" );
109 itemqueue[ in ] = *new_msg;
111 thread->set_wake_reason( Cyg_Thread::DONE );
114 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
119 // -------------------------------------------------------------------------
122 template <class T, cyg_count32 QUEUE_SIZE>
123 Cyg_Mboxt2<T,QUEUE_SIZE>::Cyg_Mboxt2()
125 CYG_REPORT_FUNCTION();
131 // -------------------------------------------------------------------------
134 template <class T, cyg_count32 QUEUE_SIZE>
135 Cyg_Mboxt2<T,QUEUE_SIZE>::~Cyg_Mboxt2()
137 CYG_REPORT_FUNCTION();
139 CYG_ASSERT( 0 == count, "Deleting mboxt2 with messages");
140 CYG_ASSERT( get_threadq.empty(), "Deleting mboxt2 with threads waiting to get");
141 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
142 CYG_ASSERT( put_threadq.empty(), "Deleting mboxt2 with threads waiting to put");
145 // Prevent preemption
146 Cyg_Scheduler::lock();
148 while ( ! get_threadq.empty() ) {
149 Cyg_Thread *thread = get_threadq.dequeue();
150 thread->set_wake_reason( Cyg_Thread::DESTRUCT );
153 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
154 while ( ! put_threadq.empty() ) {
155 Cyg_Thread *thread = put_threadq.dequeue();
156 thread->set_wake_reason( Cyg_Thread::DESTRUCT );
161 // Unlock the scheduler and maybe switch threads
162 Cyg_Scheduler::unlock();
166 // -------------------------------------------------------------------------
167 // debugging/assert function
169 #ifdef CYGDBG_USE_ASSERTS
171 template <class T, cyg_count32 QUEUE_SIZE>
173 Cyg_Mboxt2<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
175 if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
176 // then the whole thing is invalid, and we know it.
177 // so return OK, since this check should NOT make an error.
180 // check that we have a non-NULL pointer first
181 if( this == NULL ) return false;
183 #if 0 // thread queues do not have checking funcs.
184 if ( ! get_threadq.check_this( zeal ) ) return false;
185 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
186 if ( ! put_threadq.check_this( zeal ) ) return false;
192 case cyg_system_test:
197 // plenty of scope for fencepost problems here
198 if ( size < count ) return false;
199 if ( size <= base ) return false;
200 if ( 0 > count ) return false;
201 if ( 0 > base ) return false;
203 // Comments about needing 2 queues elided; they're not true in this
204 // immediate-dispatch model. I think we could get away with only
205 // one queue now, biut is it worth it? 4 bytes of redundant info
206 // buys a lot of correctness.
219 // -------------------------------------------------------------------------
220 // From here downwards, these are the major functions of the template; if
221 // being genuinely used as a template they should probably not be inlined.
222 // If being used to construct a specific class, with explicit functions,
223 // then they should be. This is controlled by:
225 #ifdef CYGIMP_MBOXT_INLINE
226 #define CYG_MBOXT_INLINE inline
228 #define CYG_MBOXT_INLINE
231 // -------------------------------------------------------------------------
232 // Get an item, or wait for one to arrive
234 template <class T, cyg_count32 QUEUE_SIZE>
235 CYG_MBOXT_INLINE cyg_bool
236 Cyg_Mboxt2<T,QUEUE_SIZE>::get( T &ritem )
238 CYG_REPORT_FUNCTION();
239 Cyg_Thread *self = Cyg_Thread::self();
241 // Prevent preemption
242 Cyg_Scheduler::lock();
244 CYG_ASSERTCLASS( this, "Bad this pointer");
246 CYG_INSTRUMENT_MBOXT(GET, this, count);
249 CYG_INSTRUMENT_MBOXT(GOT, this, count);
251 ritem = itemqueue[ (count--, base++) ];
252 CYG_ASSERT( 0 <= count, "Count went -ve" );
253 CYG_ASSERT( size >= base, "Base overflow" );
258 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
262 CYG_ASSERTCLASS( this, "Bad this pointer");
264 // Unlock the scheduler and definitely switch threads
265 Cyg_Scheduler::unlock();
267 CYG_REPORT_RETVAL( true );
271 self->set_wait_info( (CYG_ADDRWORD)&ritem );
272 self->set_sleep_reason( Cyg_Thread::WAIT );
274 get_threadq.enqueue( self );
276 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
278 CYG_ASSERTCLASS( this, "Bad this pointer");
280 // Unlock scheduler and allow other threads to run
281 Cyg_Scheduler::unlock_reschedule();
283 cyg_bool result = true;
284 switch( self->get_wake_reason() )
286 case Cyg_Thread::DESTRUCT:
287 case Cyg_Thread::BREAK:
291 case Cyg_Thread::EXIT:
299 CYG_REPORT_RETVAL( result );
304 // -------------------------------------------------------------------------
305 // Try to get an item with an absolute timeout and return success.
307 #ifdef CYGFUN_KERNEL_THREADS_TIMER
308 template <class T, cyg_count32 QUEUE_SIZE>
309 CYG_MBOXT_INLINE cyg_bool
310 Cyg_Mboxt2<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
312 CYG_REPORT_FUNCTION();
314 Cyg_Thread *self = Cyg_Thread::self();
316 // Prevent preemption
317 Cyg_Scheduler::lock();
319 CYG_ASSERTCLASS( this, "Bad this pointer");
321 CYG_INSTRUMENT_MBOXT(GET, this, count);
324 CYG_INSTRUMENT_MBOXT(GOT, this, count);
326 ritem = itemqueue[ (count--, base++) ];
327 CYG_ASSERT( 0 <= count, "Count went -ve" );
328 CYG_ASSERT( size >= base, "Base overflow" );
333 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
337 CYG_ASSERTCLASS( this, "Bad this pointer");
339 // Unlock the scheduler and maybe switch threads
340 Cyg_Scheduler::unlock();
342 CYG_REPORT_RETVAL( true );
347 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
349 // If the timeout is in the past, the wake reason will have been set to
350 // something other than NONE already. If so, skip the wait and go
351 // straight to unlock.
353 if( Cyg_Thread::NONE == self->get_wake_reason() ) {
354 self->set_wait_info( (CYG_ADDRWORD)&ritem );
356 get_threadq.enqueue( self );
358 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
361 CYG_ASSERTCLASS( this, "Bad this pointer");
363 // Unlock scheduler and allow other threads to run
364 Cyg_Scheduler::unlock_reschedule();
366 // clear the timer; if it actually fired, no worries.
369 cyg_bool result = true;
370 switch( self->get_wake_reason() )
372 case Cyg_Thread::TIMEOUT:
374 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
377 case Cyg_Thread::DESTRUCT:
378 case Cyg_Thread::BREAK:
382 case Cyg_Thread::EXIT:
390 CYG_REPORT_RETVAL( result );
393 #endif // CYGFUN_KERNEL_THREADS_TIMER
395 // -------------------------------------------------------------------------
396 // Try to get an item and return success.
398 template <class T, cyg_count32 QUEUE_SIZE>
399 CYG_MBOXT_INLINE cyg_bool
400 Cyg_Mboxt2<T,QUEUE_SIZE>::tryget( T &ritem )
402 CYG_REPORT_FUNCTION();
404 // Prevent preemption
405 Cyg_Scheduler::lock();
407 CYG_ASSERTCLASS( this, "Bad this pointer");
409 CYG_INSTRUMENT_MBOXT(TRY, this, count);
411 cyg_bool result = ( 0 < count );
412 // If the mboxt2 is not empty, grab an item and return it.
414 ritem = itemqueue[ (count--, base++) ];
415 CYG_ASSERT( 0 <= count, "Count went -ve" );
416 CYG_ASSERT( size >= base, "Base overflow" );
420 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
425 // Unlock the scheduler and maybe switch threads
426 Cyg_Scheduler::unlock();
427 CYG_REPORT_RETVAL( result );
431 // -------------------------------------------------------------------------
432 // get next item without removing it
433 template <class T, cyg_count32 QUEUE_SIZE>
434 CYG_MBOXT_INLINE cyg_bool
435 Cyg_Mboxt2<T,QUEUE_SIZE>::peek_item( T &ritem )
437 CYG_REPORT_FUNCTION();
439 // Prevent preemption
440 Cyg_Scheduler::lock();
442 CYG_ASSERTCLASS( this, "Bad this pointer");
444 CYG_INSTRUMENT_MBOXT(TRY, this, count);
446 cyg_bool result = ( 0 < count );
447 // If the mboxt2 is not empty, grab an item and return it.
449 ritem = itemqueue[ base ];
451 // Unlock the scheduler and maybe switch threads
452 Cyg_Scheduler::unlock();
453 CYG_REPORT_RETVAL( result );
457 // -------------------------------------------------------------------------
458 // Put an item in the queue; wait if full.
460 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
461 template <class T, cyg_count32 QUEUE_SIZE>
462 CYG_MBOXT_INLINE cyg_bool
463 Cyg_Mboxt2<T,QUEUE_SIZE>::put( const T item )
465 CYG_REPORT_FUNCTION();
467 Cyg_Thread *self = Cyg_Thread::self();
469 // Prevent preemption
470 Cyg_Scheduler::lock();
472 CYG_INSTRUMENT_MBOXT(PUT, this, count);
473 CYG_ASSERTCLASS( this, "Bad this pointer");
475 if ( size == count ) {
476 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
478 self->set_wait_info( (CYG_ADDRWORD)&item );
479 self->set_sleep_reason( Cyg_Thread::WAIT );
481 put_threadq.enqueue( self );
483 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
485 CYG_ASSERTCLASS( this, "Bad this pointer");
487 // when this returns, our item is in the queue.
488 Cyg_Scheduler::unlock_reschedule(); // unlock, switch threads
490 cyg_bool result = true;
491 switch( self->get_wake_reason() )
493 case Cyg_Thread::DESTRUCT:
494 case Cyg_Thread::BREAK:
498 case Cyg_Thread::EXIT:
505 CYG_REPORT_RETVAL( result );
509 if ( !get_threadq.empty() ) {
510 wakeup_winner( item );
511 CYG_ASSERTCLASS( this, "Bad this pointer");
512 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
513 CYG_REPORT_RETVAL( true );
517 cyg_count32 in = base + (count++);
521 CYG_ASSERT( size > in, "in overflow" );
522 CYG_ASSERT( 0 <= in, "in overflow" );
523 CYG_ASSERT( size >= count, "count overflow" );
525 itemqueue[ in ] = item;
527 CYG_ASSERTCLASS( this, "Bad this pointer");
529 // Unlock the scheduler and maybe switch threads
530 Cyg_Scheduler::unlock();
531 CYG_REPORT_RETVAL( true );
535 // -------------------------------------------------------------------------
536 // Put an item in the queue; wait if full, with an absolute timeout;
539 #ifdef CYGFUN_KERNEL_THREADS_TIMER
540 template <class T, cyg_count32 QUEUE_SIZE>
541 CYG_MBOXT_INLINE cyg_bool
542 Cyg_Mboxt2<T,QUEUE_SIZE>::put( const T item, cyg_tick_count abs_timeout )
544 CYG_REPORT_FUNCTION();
546 Cyg_Thread *self = Cyg_Thread::self();
548 // Prevent preemption
549 Cyg_Scheduler::lock();
551 CYG_INSTRUMENT_MBOXT(PUT, this, count);
552 CYG_ASSERTCLASS( this, "Bad this pointer");
554 if ( size == count ) {
556 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
559 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
561 // If the timeout is in the past, the wake reason will have been set to
562 // something other than NONE already. If so, skip the wait and go
563 // straight to unlock.
565 if( Cyg_Thread::NONE == self->get_wake_reason() ) {
566 self->set_wait_info( (CYG_ADDRWORD)&item );
568 put_threadq.enqueue( self );
570 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
573 CYG_ASSERTCLASS( this, "Bad this pointer");
575 // when this returns, our item is in the queue.
576 Cyg_Scheduler::unlock_reschedule(); // unlock, switch threads
578 // clear the timer; if it actually fired, no worries.
581 cyg_bool result = true;
582 switch( self->get_wake_reason() )
584 case Cyg_Thread::TIMEOUT:
586 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
589 case Cyg_Thread::DESTRUCT:
590 case Cyg_Thread::BREAK:
594 case Cyg_Thread::EXIT:
602 CYG_REPORT_RETVAL( result );
607 if ( !get_threadq.empty() ) {
608 wakeup_winner( item );
609 CYG_ASSERTCLASS( this, "Bad this pointer");
610 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
611 CYG_REPORT_RETVAL( true );
615 cyg_count32 in = base + (count++);
619 CYG_ASSERT( size > in, "in overflow" );
620 CYG_ASSERT( 0 <= in, "in overflow" );
621 CYG_ASSERT( size >= count, "count overflow" );
623 itemqueue[ in ] = item;
625 CYG_ASSERTCLASS( this, "Bad this pointer");
627 // Unlock the scheduler and maybe switch threads
628 Cyg_Scheduler::unlock();
629 CYG_REPORT_RETVAL( true );
632 #endif // CYGFUN_KERNEL_THREADS_TIMER
633 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
635 // -------------------------------------------------------------------------
636 // Try to put an item in the queue and return success; queue may be full.
638 template <class T, cyg_count32 QUEUE_SIZE>
639 CYG_MBOXT_INLINE cyg_bool
640 Cyg_Mboxt2<T,QUEUE_SIZE>::tryput( const T item )
642 CYG_REPORT_FUNCTION();
644 // Prevent preemption
645 Cyg_Scheduler::lock();
647 CYG_INSTRUMENT_MBOXT(PUT, this, count);
648 CYG_ASSERTCLASS( this, "Bad this pointer");
650 if ( size == count ) {
651 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
652 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
653 CYG_REPORT_RETVAL( false );
654 return false; // the mboxt2 is full
657 if ( !get_threadq.empty() ) {
658 CYG_ASSERT( 0 == count, "Threads waiting AND queue not empty" );
659 wakeup_winner( item );
660 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
661 CYG_REPORT_RETVAL( true );
665 cyg_count32 in = base + (count++);
669 CYG_ASSERT( size > in, "in overflow" );
670 CYG_ASSERT( 0 <= in, "in overflow" );
671 CYG_ASSERT( size >= count, "count overflow" );
673 itemqueue[ in ] = item;
675 CYG_ASSERTCLASS( this, "Bad this pointer");
677 // Unlock the scheduler and maybe switch threads
678 Cyg_Scheduler::unlock();
680 CYG_REPORT_RETVAL( true );
685 // -------------------------------------------------------------------------
686 #endif // ifndef CYGONCE_KERNEL_MBOXT2_INL