unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / kernel / v2_0 / include / mboxt2.inl
1 #ifndef CYGONCE_KERNEL_MBOXT2_INL
2 #define CYGONCE_KERNEL_MBOXT2_INL
3 //==========================================================================
4 //
5 //      mboxt2.inl
6 //
7 //      Mboxt2 mbox template class implementation
8 //
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.
15 //
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.
19 //
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
23 // for more details.
24 //
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.
28 //
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.
35 //
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####
42 //
43 // Author(s):   hmt
44 // Contributors:        hmt
45 // Date:        1998-02-10
46 // Purpose:     Mboxt2 template implementation
47 // Description: This file contains the implementations of the mboxt2
48 //              template classes.
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
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
58
59 #include <cyg/kernel/mboxt2.hxx>       // our header
60
61 #include <cyg/kernel/thread.inl>       // thread inlines
62 #include <cyg/kernel/sched.inl>        // scheduler inlines
63 #include <cyg/kernel/clock.inl>        // clock inlines
64
65 // -------------------------------------------------------------------------
66 // inline function for awakening waiting threads
67
68 template <class T, cyg_count32 QUEUE_SIZE>
69 inline void
70 Cyg_Mboxt2<T,QUEUE_SIZE>::wakeup_winner( const T &msg )
71 {
72     CYG_ASSERT( !get_threadq.empty(), "Where did the winner go?" );
73
74     // The queue is non-empty, so grab the next thread and wake it up.
75     Cyg_Thread *thread = get_threadq.dequeue();
76
77     CYG_ASSERTCLASS( thread, "Bad thread pointer");
78
79     T *msg_ret = (T *)(thread->get_wait_info());
80     *msg_ret = msg;
81
82     thread->set_wake_reason( Cyg_Thread::DONE );
83     thread->wake();
84         
85     CYG_INSTRUMENT_MBOXT(WAKE, this, thread);        
86 }
87
88 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
89 template <class T, cyg_count32 QUEUE_SIZE>
90 inline void
91 Cyg_Mboxt2<T,QUEUE_SIZE>::wakeup_putter( void )
92 {
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();
96
97         CYG_ASSERTCLASS( thread, "Bad thread pointer");
98
99         T *new_msg = (T *)(thread->get_wait_info());
100
101         cyg_count32 in = base + (count++);
102         if ( size <= in )
103             in -= size;
104
105         CYG_ASSERT( size > in, "in overflow" );
106         CYG_ASSERT( 0 <= in, "in overflow" );
107         CYG_ASSERT( size >= count, "count overflow" );
108
109         itemqueue[ in ] = *new_msg;
110
111         thread->set_wake_reason( Cyg_Thread::DONE );
112         thread->wake();
113         
114         CYG_INSTRUMENT_MBOXT(WAKE, this, thread);        
115     }
116 }
117 #endif
118
119 // -------------------------------------------------------------------------
120 // Constructor
121
122 template <class T, cyg_count32 QUEUE_SIZE>
123 Cyg_Mboxt2<T,QUEUE_SIZE>::Cyg_Mboxt2()
124 {
125     CYG_REPORT_FUNCTION();
126     base = 0;
127     count = 0;
128     CYG_REPORT_RETURN();
129 }
130
131 // -------------------------------------------------------------------------
132 // Destructor
133
134 template <class T, cyg_count32 QUEUE_SIZE>
135 Cyg_Mboxt2<T,QUEUE_SIZE>::~Cyg_Mboxt2()
136 {
137     CYG_REPORT_FUNCTION();
138 #if 0
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");
143 #endif
144 #endif
145     // Prevent preemption
146     Cyg_Scheduler::lock();
147
148     while ( ! get_threadq.empty() ) {
149         Cyg_Thread *thread = get_threadq.dequeue();
150         thread->set_wake_reason( Cyg_Thread::DESTRUCT );
151         thread->wake();
152     }
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 );
157         thread->wake();
158     }
159 #endif
160
161     // Unlock the scheduler and maybe switch threads
162     Cyg_Scheduler::unlock();    
163     CYG_REPORT_RETURN();
164 }
165
166 // -------------------------------------------------------------------------
167 // debugging/assert function
168
169 #ifdef CYGDBG_USE_ASSERTS
170
171 template <class T, cyg_count32 QUEUE_SIZE>
172 cyg_bool 
173 Cyg_Mboxt2<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
174 {
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.
178         return true;
179
180     // check that we have a non-NULL pointer first
181     if( this == NULL ) return false;
182
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;
187 #endif
188 #endif
189
190     switch( zeal )
191     {
192     case cyg_system_test:
193     case cyg_extreme:
194     case cyg_thorough:
195     case cyg_quick:
196     case cyg_trivial:
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;
202
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.
207       
208     case cyg_none:
209     default:
210         break;
211     };
212
213     return true;
214 }
215
216 #endif
217
218
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:
224
225 #ifdef CYGIMP_MBOXT_INLINE
226 #define CYG_MBOXT_INLINE inline
227 #else
228 #define CYG_MBOXT_INLINE
229 #endif
230
231 // -------------------------------------------------------------------------
232 // Get an item, or wait for one to arrive
233
234 template <class T, cyg_count32 QUEUE_SIZE>
235 CYG_MBOXT_INLINE cyg_bool
236 Cyg_Mboxt2<T,QUEUE_SIZE>::get( T &ritem )
237 {
238     CYG_REPORT_FUNCTION();
239     Cyg_Thread *self = Cyg_Thread::self();
240     
241     // Prevent preemption
242     Cyg_Scheduler::lock();
243
244     CYG_ASSERTCLASS( this, "Bad this pointer");
245     
246     CYG_INSTRUMENT_MBOXT(GET, this, count);
247
248     if ( 0 < count ) {
249         CYG_INSTRUMENT_MBOXT(GOT, this, count);
250     
251         ritem = itemqueue[ (count--, base++) ];
252         CYG_ASSERT( 0 <= count, "Count went -ve" );
253         CYG_ASSERT( size >= base, "Base overflow" );
254
255         if ( size <= base )
256             base = 0;
257
258 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
259         wakeup_putter();
260 #endif
261
262         CYG_ASSERTCLASS( this, "Bad this pointer");
263
264         // Unlock the scheduler and definitely switch threads
265         Cyg_Scheduler::unlock();
266
267         CYG_REPORT_RETVAL( true );
268         return true;
269     }
270
271     self->set_wait_info( (CYG_ADDRWORD)&ritem );
272     self->set_sleep_reason( Cyg_Thread::WAIT );
273     self->sleep();
274     get_threadq.enqueue( self );
275
276     CYG_INSTRUMENT_MBOXT(WAIT, this, count);
277
278     CYG_ASSERTCLASS( this, "Bad this pointer");
279
280     // Unlock scheduler and allow other threads to run
281     Cyg_Scheduler::unlock_reschedule();
282
283     cyg_bool result = true;
284     switch( self->get_wake_reason() )
285     {
286     case Cyg_Thread::DESTRUCT:
287     case Cyg_Thread::BREAK:
288         result = false;
289         break;
290             
291     case Cyg_Thread::EXIT:            
292         self->exit();
293         break;
294
295     default:
296         break;
297     }
298
299     CYG_REPORT_RETVAL( result );
300     return result;
301 }
302
303
304 // -------------------------------------------------------------------------
305 // Try to get an item with an absolute timeout and return success.
306
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 )
311 {
312     CYG_REPORT_FUNCTION();
313         
314     Cyg_Thread *self = Cyg_Thread::self();
315     
316     // Prevent preemption
317     Cyg_Scheduler::lock();
318
319     CYG_ASSERTCLASS( this, "Bad this pointer");
320     
321     CYG_INSTRUMENT_MBOXT(GET, this, count);
322
323     if ( 0 < count ) {
324         CYG_INSTRUMENT_MBOXT(GOT, this, count);
325     
326         ritem = itemqueue[ (count--, base++) ];
327         CYG_ASSERT( 0 <= count, "Count went -ve" );
328         CYG_ASSERT( size >= base, "Base overflow" );
329
330         if ( size <= base )
331             base = 0;
332
333 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
334         wakeup_putter();
335 #endif
336
337         CYG_ASSERTCLASS( this, "Bad this pointer");
338
339         // Unlock the scheduler and maybe switch threads
340         Cyg_Scheduler::unlock();
341
342         CYG_REPORT_RETVAL( true );
343         return true;
344     }
345
346     // Set the timer
347     self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
348
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.
352     
353     if( Cyg_Thread::NONE == self->get_wake_reason() ) {
354         self->set_wait_info( (CYG_ADDRWORD)&ritem );
355         self->sleep();
356         get_threadq.enqueue( self );
357
358         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
359     }
360
361     CYG_ASSERTCLASS( this, "Bad this pointer");        
362
363     // Unlock scheduler and allow other threads to run
364     Cyg_Scheduler::unlock_reschedule();
365
366     // clear the timer; if it actually fired, no worries.
367     self->clear_timer();
368
369     cyg_bool result = true;
370     switch( self->get_wake_reason() )
371     {
372     case Cyg_Thread::TIMEOUT:
373         result = false;
374         CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
375         break;
376         
377     case Cyg_Thread::DESTRUCT:
378     case Cyg_Thread::BREAK:
379         result = false;
380         break;
381             
382     case Cyg_Thread::EXIT:            
383         self->exit();
384         break;
385
386     default:
387         break;
388     }
389
390     CYG_REPORT_RETVAL( result );
391     return result;
392 }
393 #endif // CYGFUN_KERNEL_THREADS_TIMER
394
395 // -------------------------------------------------------------------------
396 // Try to get an item and return success.
397
398 template <class T, cyg_count32 QUEUE_SIZE>
399 CYG_MBOXT_INLINE cyg_bool
400 Cyg_Mboxt2<T,QUEUE_SIZE>::tryget( T &ritem )
401 {
402     CYG_REPORT_FUNCTION();
403         
404     // Prevent preemption
405     Cyg_Scheduler::lock();
406
407     CYG_ASSERTCLASS( this, "Bad this pointer");
408     
409     CYG_INSTRUMENT_MBOXT(TRY, this, count);
410     
411     cyg_bool result = ( 0 < count );
412     // If the mboxt2 is not empty, grab an item and return it.
413     if ( result ) {
414         ritem = itemqueue[ (count--, base++) ];
415         CYG_ASSERT( 0 <= count, "Count went -ve" );
416         CYG_ASSERT( size >= base, "Base overflow" );
417         if ( size <= base )
418             base = 0;
419
420 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
421         wakeup_putter();
422 #endif
423     }
424
425     // Unlock the scheduler and maybe switch threads
426     Cyg_Scheduler::unlock();
427     CYG_REPORT_RETVAL( result );
428     return result;    
429 }
430
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 )
436 {
437     CYG_REPORT_FUNCTION();
438         
439     // Prevent preemption
440     Cyg_Scheduler::lock();
441
442     CYG_ASSERTCLASS( this, "Bad this pointer");
443     
444     CYG_INSTRUMENT_MBOXT(TRY, this, count);
445     
446     cyg_bool result = ( 0 < count );
447     // If the mboxt2 is not empty, grab an item and return it.
448     if ( result )
449         ritem = itemqueue[ base ];
450
451     // Unlock the scheduler and maybe switch threads
452     Cyg_Scheduler::unlock();
453     CYG_REPORT_RETVAL( result );
454     return result;    
455 }
456
457 // -------------------------------------------------------------------------
458 // Put an item in the queue; wait if full.
459
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 )
464 {
465     CYG_REPORT_FUNCTION();
466         
467     Cyg_Thread *self = Cyg_Thread::self();
468
469     // Prevent preemption
470     Cyg_Scheduler::lock();
471
472     CYG_INSTRUMENT_MBOXT(PUT, this, count);
473     CYG_ASSERTCLASS( this, "Bad this pointer");
474
475     if ( size == count ) {
476         CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
477
478         self->set_wait_info( (CYG_ADDRWORD)&item );
479         self->set_sleep_reason( Cyg_Thread::WAIT );
480         self->sleep();
481         put_threadq.enqueue( self );
482
483         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
484         
485         CYG_ASSERTCLASS( this, "Bad this pointer");    
486
487         // when this returns, our item is in the queue.
488         Cyg_Scheduler::unlock_reschedule();        // unlock, switch threads
489
490         cyg_bool result = true;
491         switch( self->get_wake_reason() )
492         {
493         case Cyg_Thread::DESTRUCT:
494         case Cyg_Thread::BREAK:
495             result = false;
496             break;
497             
498         case Cyg_Thread::EXIT:            
499             self->exit();
500             break;
501
502         default:
503             break;
504         }
505         CYG_REPORT_RETVAL( result );
506         return result;
507     }
508
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 );
514         return true;
515     }
516
517     cyg_count32 in = base + (count++);
518     if ( size <= in )
519         in -= size;
520
521     CYG_ASSERT( size > in, "in overflow" );
522     CYG_ASSERT( 0 <= in, "in overflow" );
523     CYG_ASSERT( size >= count, "count overflow" );
524
525     itemqueue[ in ] = item;
526
527     CYG_ASSERTCLASS( this, "Bad this pointer");    
528     
529     // Unlock the scheduler and maybe switch threads
530     Cyg_Scheduler::unlock();
531     CYG_REPORT_RETVAL( true );
532     return true;
533 }
534
535 // -------------------------------------------------------------------------
536 // Put an item in the queue; wait if full, with an absolute timeout;
537 // return success.
538
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 )
543 {
544     CYG_REPORT_FUNCTION();
545         
546     Cyg_Thread *self = Cyg_Thread::self();
547
548     // Prevent preemption
549     Cyg_Scheduler::lock();
550
551     CYG_INSTRUMENT_MBOXT(PUT, this, count);
552     CYG_ASSERTCLASS( this, "Bad this pointer");
553
554     if ( size == count ) {
555
556         CYG_ASSERT( get_threadq.empty(), "Threads waiting AND queue full?" );
557
558         // Set the timer
559         self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
560
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.
564     
565         if( Cyg_Thread::NONE == self->get_wake_reason() ) {
566             self->set_wait_info( (CYG_ADDRWORD)&item );
567             self->sleep();
568             put_threadq.enqueue( self );
569
570             CYG_INSTRUMENT_MBOXT(WAIT, this, count);
571         }
572
573         CYG_ASSERTCLASS( this, "Bad this pointer");    
574
575         // when this returns, our item is in the queue.
576         Cyg_Scheduler::unlock_reschedule();        // unlock, switch threads
577
578         // clear the timer; if it actually fired, no worries.
579         self->clear_timer();
580
581         cyg_bool result = true;
582         switch( self->get_wake_reason() )
583         {
584         case Cyg_Thread::TIMEOUT:
585             result = false;
586             CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
587             break;
588         
589         case Cyg_Thread::DESTRUCT:
590         case Cyg_Thread::BREAK:
591             result = false;
592             break;
593             
594         case Cyg_Thread::EXIT:            
595             self->exit();
596             break;
597
598         default:
599             break;
600         }
601
602         CYG_REPORT_RETVAL( result );
603         return result;
604     }
605
606
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 );
612         return true;
613     }
614
615     cyg_count32 in = base + (count++);
616     if ( size <= in )
617         in -= size;
618
619     CYG_ASSERT( size > in, "in overflow" );
620     CYG_ASSERT( 0 <= in, "in overflow" );
621     CYG_ASSERT( size >= count, "count overflow" );
622
623     itemqueue[ in ] = item;
624
625     CYG_ASSERTCLASS( this, "Bad this pointer");    
626
627     // Unlock the scheduler and maybe switch threads
628     Cyg_Scheduler::unlock();
629     CYG_REPORT_RETVAL( true );
630     return true;
631 }
632 #endif // CYGFUN_KERNEL_THREADS_TIMER
633 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
634
635 // -------------------------------------------------------------------------
636 // Try to put an item in the queue and return success; queue may be full.
637
638 template <class T, cyg_count32 QUEUE_SIZE>
639 CYG_MBOXT_INLINE cyg_bool
640 Cyg_Mboxt2<T,QUEUE_SIZE>::tryput( const T item )
641 {
642     CYG_REPORT_FUNCTION();
643         
644     // Prevent preemption
645     Cyg_Scheduler::lock();
646
647     CYG_INSTRUMENT_MBOXT(PUT, this, count);
648     CYG_ASSERTCLASS( this, "Bad this pointer");
649
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
655     }
656
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 );
662         return true;
663     }
664
665     cyg_count32 in = base + (count++);
666     if ( size <= in )
667         in -= size;
668
669     CYG_ASSERT( size > in, "in overflow" );
670     CYG_ASSERT( 0 <= in, "in overflow" );
671     CYG_ASSERT( size >= count, "count overflow" );
672
673     itemqueue[ in ] = item;
674
675     CYG_ASSERTCLASS( this, "Bad this pointer");    
676
677     // Unlock the scheduler and maybe switch threads
678     Cyg_Scheduler::unlock();
679
680     CYG_REPORT_RETVAL( true );
681     return true;
682 }
683
684
685 // -------------------------------------------------------------------------
686 #endif // ifndef CYGONCE_KERNEL_MBOXT2_INL
687 // EOF mboxt2.inl