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.
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: Mboxt2 template implementation
49 // Description: This file contains the implementations of the mboxt2
52 //####DESCRIPTIONEND####
54 //==========================================================================
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/mboxt2.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_Mboxt2<T,QUEUE_SIZE>::wakeup_winner( const T &msg )
74 CYG_ASSERT( !get_threadq.empty(), "Where did the winner go?" );
76 // The queue is non-empty, so grab the next thread and wake it up.
77 Cyg_Thread *thread = get_threadq.dequeue();
79 CYG_ASSERTCLASS( thread, "Bad thread pointer");
81 T *msg_ret = (T *)(thread->get_wait_info());
84 thread->set_wake_reason( Cyg_Thread::DONE );
87 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
90 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
91 template <class T, cyg_count32 QUEUE_SIZE>
93 Cyg_Mboxt2<T,QUEUE_SIZE>::wakeup_putter( void )
95 if( !put_threadq.empty() ) {
96 // The queue is non-empty, so grab the next thread and wake it up.
97 Cyg_Thread *thread = put_threadq.dequeue();
99 CYG_ASSERTCLASS( thread, "Bad thread pointer");
101 T *new_msg = (T *)(thread->get_wait_info());
103 cyg_count32 in = base + (count++);
107 CYG_ASSERT( size > in, "in overflow" );
108 CYG_ASSERT( 0 <= in, "in overflow" );
109 CYG_ASSERT( size >= count, "count overflow" );
111 itemqueue[ in ] = *new_msg;
113 thread->set_wake_reason( Cyg_Thread::DONE );
116 CYG_INSTRUMENT_MBOXT(WAKE, this, thread);
121 // -------------------------------------------------------------------------
124 template <class T, cyg_count32 QUEUE_SIZE>
125 Cyg_Mboxt2<T,QUEUE_SIZE>::Cyg_Mboxt2()
127 CYG_REPORT_FUNCTION();
133 // -------------------------------------------------------------------------
136 template <class T, cyg_count32 QUEUE_SIZE>
137 Cyg_Mboxt2<T,QUEUE_SIZE>::~Cyg_Mboxt2()
139 CYG_REPORT_FUNCTION();
141 CYG_ASSERT( 0 == count, "Deleting mboxt2 with messages");
142 CYG_ASSERT( get_threadq.empty(), "Deleting mboxt2 with threads waiting to get");
143 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
144 CYG_ASSERT( put_threadq.empty(), "Deleting mboxt2 with threads waiting to put");
147 // Prevent preemption
148 Cyg_Scheduler::lock();
150 while ( ! get_threadq.empty() ) {
151 Cyg_Thread *thread = get_threadq.dequeue();
152 thread->set_wake_reason( Cyg_Thread::DESTRUCT );
155 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
156 while ( ! put_threadq.empty() ) {
157 Cyg_Thread *thread = put_threadq.dequeue();
158 thread->set_wake_reason( Cyg_Thread::DESTRUCT );
163 // Unlock the scheduler and maybe switch threads
164 Cyg_Scheduler::unlock();
168 // -------------------------------------------------------------------------
169 // debugging/assert function
171 #ifdef CYGDBG_USE_ASSERTS
173 template <class T, cyg_count32 QUEUE_SIZE>
175 Cyg_Mboxt2<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
177 if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
178 // then the whole thing is invalid, and we know it.
179 // so return OK, since this check should NOT make an error.
182 // check that we have a non-NULL pointer first
183 if( this == NULL ) return false;
185 #if 0 // thread queues do not have checking funcs.
186 if ( ! get_threadq.check_this( zeal ) ) return false;
187 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
188 if ( ! put_threadq.check_this( zeal ) ) return false;
194 case cyg_system_test:
199 // plenty of scope for fencepost problems here
200 if ( size < count ) return false;
201 if ( size <= base ) return false;
202 if ( 0 > count ) return false;
203 if ( 0 > base ) return false;
205 // Comments about needing 2 queues elided; they're not true in this
206 // immediate-dispatch model. I think we could get away with only
207 // one queue now, biut is it worth it? 4 bytes of redundant info
208 // buys a lot of correctness.
221 // -------------------------------------------------------------------------
222 // From here downwards, these are the major functions of the template; if
223 // being genuinely used as a template they should probably not be inlined.
224 // If being used to construct a specific class, with explicit functions,
225 // then they should be. This is controlled by:
227 #ifdef CYGIMP_MBOXT_INLINE
228 #define CYG_MBOXT_INLINE inline
230 #define CYG_MBOXT_INLINE
233 // -------------------------------------------------------------------------
234 // Get an item, or wait for one to arrive
236 template <class T, cyg_count32 QUEUE_SIZE>
237 CYG_MBOXT_INLINE cyg_bool
238 Cyg_Mboxt2<T,QUEUE_SIZE>::get( T &ritem )
240 CYG_REPORT_FUNCTION();
241 Cyg_Thread *self = Cyg_Thread::self();
243 // Prevent preemption
244 Cyg_Scheduler::lock();
246 CYG_ASSERTCLASS( this, "Bad this pointer");
248 CYG_INSTRUMENT_MBOXT(GET, this, count);
251 CYG_INSTRUMENT_MBOXT(GOT, this, count);
253 ritem = itemqueue[ (count--, base++) ];
254 CYG_ASSERT( 0 <= count, "Count went -ve" );
255 CYG_ASSERT( size >= base, "Base overflow" );
260 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
264 // Unlock the scheduler and definitely switch threads
265 Cyg_Scheduler::unlock();
267 CYG_ASSERTCLASS( this, "Bad this pointer");
268 CYG_REPORT_RETVAL( true );
272 self->set_wait_info( (CYG_ADDRWORD)&ritem );
273 self->set_sleep_reason( Cyg_Thread::WAIT );
275 get_threadq.enqueue( self );
277 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
279 // Unlock scheduler and allow other threads to run
280 Cyg_Scheduler::unlock_reschedule();
282 cyg_bool result = true;
283 switch( self->get_wake_reason() )
285 case Cyg_Thread::DESTRUCT:
286 case Cyg_Thread::BREAK:
290 case Cyg_Thread::EXIT:
297 CYG_ASSERTCLASS( this, "Bad this pointer");
298 CYG_REPORT_RETVAL( result );
303 // -------------------------------------------------------------------------
304 // Try to get an item with an absolute timeout and return success.
306 #ifdef CYGFUN_KERNEL_THREADS_TIMER
307 template <class T, cyg_count32 QUEUE_SIZE>
308 CYG_MBOXT_INLINE cyg_bool
309 Cyg_Mboxt2<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
311 CYG_REPORT_FUNCTION();
313 Cyg_Thread *self = Cyg_Thread::self();
315 // Prevent preemption
316 Cyg_Scheduler::lock();
318 CYG_ASSERTCLASS( this, "Bad this pointer");
320 CYG_INSTRUMENT_MBOXT(GET, this, count);
323 CYG_INSTRUMENT_MBOXT(GOT, this, count);
325 ritem = itemqueue[ (count--, base++) ];
326 CYG_ASSERT( 0 <= count, "Count went -ve" );
327 CYG_ASSERT( size >= base, "Base overflow" );
332 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
336 // Unlock the scheduler and maybe switch threads
337 Cyg_Scheduler::unlock();
339 CYG_ASSERTCLASS( this, "Bad this pointer");
340 CYG_REPORT_RETVAL( true );
345 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
347 // If the timeout is in the past, the wake reason will have been set to
348 // something other than NONE already. If so, skip the wait and go
349 // straight to unlock.
351 if( Cyg_Thread::NONE == self->get_wake_reason() ) {
352 self->set_wait_info( (CYG_ADDRWORD)&ritem );
354 get_threadq.enqueue( self );
356 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
359 // Unlock scheduler and allow other threads to run
360 Cyg_Scheduler::unlock_reschedule();
362 // clear the timer; if it actually fired, no worries.
365 CYG_ASSERTCLASS( this, "Bad this pointer");
367 cyg_bool result = true;
368 switch( self->get_wake_reason() )
370 case Cyg_Thread::TIMEOUT:
372 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
375 case Cyg_Thread::DESTRUCT:
376 case Cyg_Thread::BREAK:
380 case Cyg_Thread::EXIT:
388 CYG_REPORT_RETVAL( result );
391 #endif // CYGFUN_KERNEL_THREADS_TIMER
393 // -------------------------------------------------------------------------
394 // Try to get an item and return success.
396 template <class T, cyg_count32 QUEUE_SIZE>
397 CYG_MBOXT_INLINE cyg_bool
398 Cyg_Mboxt2<T,QUEUE_SIZE>::tryget( T &ritem )
400 CYG_REPORT_FUNCTION();
402 CYG_ASSERTCLASS( this, "Bad this pointer");
404 // Prevent preemption
405 Cyg_Scheduler::lock();
407 CYG_INSTRUMENT_MBOXT(TRY, this, count);
409 cyg_bool result = ( 0 < count );
410 // If the mboxt2 is not empty, grab an item and return it.
412 ritem = itemqueue[ (count--, base++) ];
413 CYG_ASSERT( 0 <= count, "Count went -ve" );
414 CYG_ASSERT( size >= base, "Base overflow" );
418 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
423 // Unlock the scheduler and maybe switch threads
424 Cyg_Scheduler::unlock();
425 CYG_REPORT_RETVAL( result );
429 // -------------------------------------------------------------------------
430 // get next item without removing it
431 template <class T, cyg_count32 QUEUE_SIZE>
432 CYG_MBOXT_INLINE cyg_bool
433 Cyg_Mboxt2<T,QUEUE_SIZE>::peek_item( T &ritem )
435 CYG_REPORT_FUNCTION();
437 CYG_ASSERTCLASS( this, "Bad this pointer");
439 // Prevent preemption
440 Cyg_Scheduler::lock();
442 CYG_INSTRUMENT_MBOXT(TRY, this, count);
444 cyg_bool result = ( 0 < count );
445 // If the mboxt2 is not empty, grab an item and return it.
447 ritem = itemqueue[ base ];
449 // Unlock the scheduler and maybe switch threads
450 Cyg_Scheduler::unlock();
451 CYG_REPORT_RETVAL( result );
455 // -------------------------------------------------------------------------
456 // Put an item in the queue; wait if full.
458 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
459 template <class T, cyg_count32 QUEUE_SIZE>
460 CYG_MBOXT_INLINE cyg_bool
461 Cyg_Mboxt2<T,QUEUE_SIZE>::put( const T item )
463 CYG_REPORT_FUNCTION();
465 Cyg_Thread *self = Cyg_Thread::self();
467 // Prevent preemption
468 Cyg_Scheduler::lock();
470 CYG_INSTRUMENT_MBOXT(PUT, this, count);
471 CYG_ASSERTCLASS( this, "Bad this pointer");
473 if ( size == count ) {
474 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
476 self->set_wait_info( (CYG_ADDRWORD)&item );
477 self->set_sleep_reason( Cyg_Thread::WAIT );
479 put_threadq.enqueue( self );
481 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
483 // when this returns, our item is in the queue.
484 Cyg_Scheduler::unlock_reschedule(); // unlock, switch threads
486 CYG_ASSERTCLASS( this, "Bad this pointer");
488 cyg_bool result = true;
489 switch( self->get_wake_reason() )
491 case Cyg_Thread::DESTRUCT:
492 case Cyg_Thread::BREAK:
496 case Cyg_Thread::EXIT:
503 CYG_REPORT_RETVAL( result );
507 if ( !get_threadq.empty() ) {
508 wakeup_winner( item );
509 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
510 CYG_ASSERTCLASS( this, "Bad this pointer");
511 CYG_REPORT_RETVAL( true );
515 cyg_count32 in = base + (count++);
519 CYG_ASSERT( size > in, "in overflow" );
520 CYG_ASSERT( 0 <= in, "in overflow" );
521 CYG_ASSERT( size >= count, "count overflow" );
523 itemqueue[ in ] = item;
525 CYG_ASSERTCLASS( this, "Bad this pointer");
527 // Unlock the scheduler and maybe switch threads
528 Cyg_Scheduler::unlock();
529 CYG_REPORT_RETVAL( true );
533 // -------------------------------------------------------------------------
534 // Put an item in the queue; wait if full, with an absolute timeout;
537 #ifdef CYGFUN_KERNEL_THREADS_TIMER
538 template <class T, cyg_count32 QUEUE_SIZE>
539 CYG_MBOXT_INLINE cyg_bool
540 Cyg_Mboxt2<T,QUEUE_SIZE>::put( const T item, cyg_tick_count abs_timeout )
542 CYG_REPORT_FUNCTION();
544 Cyg_Thread *self = Cyg_Thread::self();
546 // Prevent preemption
547 Cyg_Scheduler::lock();
549 CYG_INSTRUMENT_MBOXT(PUT, this, count);
550 CYG_ASSERTCLASS( this, "Bad this pointer");
552 if ( size == count ) {
554 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
557 self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
559 // If the timeout is in the past, the wake reason will have been set to
560 // something other than NONE already. If so, skip the wait and go
561 // straight to unlock.
563 if( Cyg_Thread::NONE == self->get_wake_reason() ) {
564 self->set_wait_info( (CYG_ADDRWORD)&item );
566 put_threadq.enqueue( self );
568 CYG_INSTRUMENT_MBOXT(WAIT, this, count);
571 // when this returns, our item is in the queue.
572 Cyg_Scheduler::unlock_reschedule(); // unlock, switch threads
574 // clear the timer; if it actually fired, no worries.
577 cyg_bool result = true;
578 switch( self->get_wake_reason() )
580 case Cyg_Thread::TIMEOUT:
582 CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
585 case Cyg_Thread::DESTRUCT:
586 case Cyg_Thread::BREAK:
590 case Cyg_Thread::EXIT:
598 CYG_ASSERTCLASS( this, "Bad this pointer");
599 CYG_REPORT_RETVAL( result );
604 if ( !get_threadq.empty() ) {
605 wakeup_winner( item );
606 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
607 CYG_ASSERTCLASS( this, "Bad this pointer");
608 CYG_REPORT_RETVAL( true );
612 cyg_count32 in = base + (count++);
616 CYG_ASSERT( size > in, "in overflow" );
617 CYG_ASSERT( 0 <= in, "in overflow" );
618 CYG_ASSERT( size >= count, "count overflow" );
620 itemqueue[ in ] = item;
622 // Unlock the scheduler and maybe switch threads
623 Cyg_Scheduler::unlock();
624 CYG_ASSERTCLASS( this, "Bad this pointer");
625 CYG_REPORT_RETVAL( true );
628 #endif // CYGFUN_KERNEL_THREADS_TIMER
629 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
631 // -------------------------------------------------------------------------
632 // Try to put an item in the queue and return success; queue may be full.
634 template <class T, cyg_count32 QUEUE_SIZE>
635 CYG_MBOXT_INLINE cyg_bool
636 Cyg_Mboxt2<T,QUEUE_SIZE>::tryput( const T item )
638 CYG_REPORT_FUNCTION();
640 // Prevent preemption
641 Cyg_Scheduler::lock();
643 CYG_INSTRUMENT_MBOXT(PUT, this, count);
644 CYG_ASSERTCLASS( this, "Bad this pointer");
646 if ( size == count ) {
647 CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
648 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
649 CYG_REPORT_RETVAL( false );
650 return false; // the mboxt2 is full
653 if ( !get_threadq.empty() ) {
654 CYG_ASSERT( 0 == count, "Threads waiting AND queue not empty" );
655 wakeup_winner( item );
656 Cyg_Scheduler::unlock(); // unlock, maybe switch threads
657 CYG_REPORT_RETVAL( true );
661 cyg_count32 in = base + (count++);
665 CYG_ASSERT( size > in, "in overflow" );
666 CYG_ASSERT( 0 <= in, "in overflow" );
667 CYG_ASSERT( size >= count, "count overflow" );
669 itemqueue[ in ] = item;
671 CYG_ASSERTCLASS( this, "Bad this pointer");
673 // Unlock the scheduler and maybe switch threads
674 Cyg_Scheduler::unlock();
676 CYG_REPORT_RETVAL( true );
681 // -------------------------------------------------------------------------
682 #endif // ifndef CYGONCE_KERNEL_MBOXT2_INL