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