]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/kernel/v2_0/include/mboxt.inl
Initial revision
[karo-tx-redboot.git] / packages / kernel / v2_0 / include / mboxt.inl
1 #ifndef CYGONCE_KERNEL_MBOXT_INL
2 #define CYGONCE_KERNEL_MBOXT_INL
3 //==========================================================================
4 //
5 //      mboxt.inl
6 //
7 //      Mboxt 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:     Mboxt template implementation
49 // Description: This file contains the implementations of the mboxt
50 //              template classes.
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <pkgconf/kernel.h>
57
58 #include <cyg/kernel/ktypes.h>         // base kernel types
59 #include <cyg/infra/cyg_trac.h>        // tracing macros
60 #include <cyg/infra/cyg_ass.h>         // assertion macros
61 #include <cyg/kernel/instrmnt.h>       // instrumentation
62
63 #include <cyg/kernel/mboxt.hxx>        // our header
64
65 #include <cyg/kernel/thread.inl>       // thread inlines
66 #include <cyg/kernel/sched.inl>        // scheduler inlines
67 #include <cyg/kernel/clock.inl>        // clock inlines
68
69 // -------------------------------------------------------------------------
70 // inline function for awakening waiting threads
71
72 template <class T, cyg_count32 QUEUE_SIZE>
73 inline void
74 Cyg_Mboxt<T,QUEUE_SIZE>::wakeup_waiter( Cyg_ThreadQueue &q )
75 {
76     if( !q.empty() ) {
77         // The queue is non-empty, so grab the next thread and wake it up.
78         Cyg_Thread *thread = q.dequeue();
79
80         CYG_ASSERTCLASS( thread, "Bad thread pointer");
81
82         thread->set_wake_reason( Cyg_Thread::DONE );
83         thread->wake();
84         CYG_INSTRUMENT_MBOXT(WAKE, this, thread);        
85     }
86 }
87
88 // -------------------------------------------------------------------------
89 // Constructor
90
91 template <class T, cyg_count32 QUEUE_SIZE>
92 Cyg_Mboxt<T,QUEUE_SIZE>::Cyg_Mboxt()
93 {
94     CYG_REPORT_FUNCTION();
95     base = 0;
96     count = 0;
97 }
98
99 // -------------------------------------------------------------------------
100 // Destructor
101
102 template <class T, cyg_count32 QUEUE_SIZE>
103 Cyg_Mboxt<T,QUEUE_SIZE>::~Cyg_Mboxt()
104 {
105     CYG_REPORT_FUNCTION();
106     CYG_ASSERT( 0 == count, "Deleting mboxt with messages");
107     CYG_ASSERT( get_threadq.empty(), "Deleting mboxt with threads waiting to get");
108 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
109     CYG_ASSERT( put_threadq.empty(), "Deleting mboxt with threads waiting to put");
110 #endif
111 }
112
113 // -------------------------------------------------------------------------
114 // debugging/assert function
115
116 #ifdef CYGDBG_USE_ASSERTS
117
118 template <class T, cyg_count32 QUEUE_SIZE>
119 cyg_bool 
120 Cyg_Mboxt<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
121 {
122     CYG_REPORT_FUNCTION();
123         
124     if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
125         // then the whole thing is invalid, and we know it.
126         // so return OK, since this check should NOT make an error.
127         return true;
128
129     // check that we have a non-NULL pointer first
130     if( this == NULL ) return false;
131
132 #if 0 // thread queues do not have checking funcs.
133     if ( ! get_threadq.check_this( zeal ) ) return false;
134 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
135     if ( ! put_threadq.check_this( zeal ) ) return false;
136 #endif
137 #endif
138
139     switch( zeal )
140     {
141     case cyg_system_test:
142     case cyg_extreme:
143     case cyg_thorough:
144     case cyg_quick:
145     case cyg_trivial:
146         // plenty of scope for fencepost problems here
147         if ( size < count ) return false;
148         if ( size <= base ) return false;
149         if ( 0 > count ) return false;
150         if ( 0 > base  ) return false;
151       
152         // there was initially a test of the form
153         //    (0 < count && count < size) && ! threadqueue.empty()
154         // here - ie. there should only be people waiting if the Q is full
155         // or empty.  This is bogus, anyone else might run between a waiter
156         // being awoken, so there can be a 2nd waiter in the Q and a free
157         // slot (say) simultaneously.
158
159         // Further, we need 2 queues; imagine a 10-slot itemqueue with 25
160         // attempts to put to it, so 15 sleep.  10 other threads get,
161         // awakening 10 of the 15 put-sleepers.  Another one gets, and
162         // can't because there is no data there _yet_; it sleeps, and the
163         // 10 awakened threads cycle through the run queue, each putting,
164         // the first awakens the get-sleeper, which in turn awakens a
165         // further put-sleeper.
166
167         // This requirement for 2 queue only holds if Ngetters > 2 * Nslots
168         // or Nputters > 2 * Nslots; if these are both false, one queue
169         // will suffice.  This could be an optimisation for the future -
170         // wow, 4 bytes.
171
172     case cyg_none:
173     default:
174         break;
175     };
176
177     return true;
178 }
179
180 #endif
181
182
183 // -------------------------------------------------------------------------
184 // From here downwards, these are the major functions of the template; if
185 // being genuinely used as a template they should probably not be inlined.
186 // If being used to construct a specific class, with explicit functions,
187 // then they should be.  This is controlled by:
188
189 #ifdef CYGIMP_MBOXT_INLINE
190 #define CYG_MBOXT_INLINE inline
191 #else
192 #define CYG_MBOXT_INLINE
193 #endif
194
195 // -------------------------------------------------------------------------
196 // Get an item, or wait for one to arrive
197
198 template <class T, cyg_count32 QUEUE_SIZE>
199 CYG_MBOXT_INLINE cyg_bool
200 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem )
201 {
202     CYG_REPORT_FUNCTION();
203     cyg_bool result = true;
204         
205     Cyg_Thread *self = Cyg_Thread::self();
206     
207     // Prevent preemption
208     Cyg_Scheduler::lock();
209
210     CYG_ASSERTCLASS( this, "Bad this pointer");
211     
212     CYG_INSTRUMENT_MBOXT(GET, this, count);
213
214     // Loop while the mboxt is empty, sleeping each time around
215     // the loop. This copes with the possibility of a higher priority
216     // thread grabbing the message between the wakeup in unlock() and
217     // this thread actually starting.
218     
219     while( result && (0 == count) ) {
220         self->set_sleep_reason( Cyg_Thread::WAIT );
221         self->sleep();
222         get_threadq.enqueue( self );
223
224         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
225         
226         // Allow other threads to run
227         Cyg_Scheduler::reschedule();
228
229         CYG_ASSERTCLASS( this, "Bad this pointer");        
230
231         switch( self->get_wake_reason() )
232         {
233         case Cyg_Thread::DESTRUCT:
234         case Cyg_Thread::BREAK:
235             result = false;
236             break;
237             
238         case Cyg_Thread::EXIT:            
239             self->exit();
240             break;
241
242         default:
243             break;
244         }
245     }
246
247     if ( result ) {
248         CYG_INSTRUMENT_MBOXT(GOT, this, count);
249     
250         ritem = itemqueue[ (count--, base++) ];
251         CYG_ASSERT( 0 <= count, "Count went -ve" );
252         CYG_ASSERT( size >= base, "Base overflow" );
253
254         if ( size <= base )
255             base = 0;
256
257 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
258         wakeup_waiter( put_threadq );
259 #endif
260     }
261
262     // Unlock the scheduler and maybe switch threads
263     Cyg_Scheduler::unlock();
264
265     CYG_ASSERTCLASS( this, "Bad this pointer");
266     CYG_REPORT_RETVAL( result );
267     return result;
268 }
269
270
271 // -------------------------------------------------------------------------
272 // Try to get an item with an absolute timeout and return success.
273
274 #ifdef CYGFUN_KERNEL_THREADS_TIMER
275 template <class T, cyg_count32 QUEUE_SIZE>
276 CYG_MBOXT_INLINE cyg_bool
277 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
278 {
279     CYG_REPORT_FUNCTION();
280     cyg_bool result = true;
281         
282     Cyg_Thread *self = Cyg_Thread::self();
283     
284     // Prevent preemption
285     Cyg_Scheduler::lock();
286
287     CYG_ASSERTCLASS( this, "Bad this pointer");
288     
289     CYG_INSTRUMENT_MBOXT(GET, this, count);
290
291     // Set the timer _once_ outside the loop.
292     self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT  );
293
294     // If the timeout is in the past, the wake reason will have been
295     // set to something other than NONE already. Set the result false
296     // to force an immediate return.
297     
298     if( self->get_wake_reason() != Cyg_Thread::NONE )
299         result = false;
300                 
301     // Loop while the mboxt is empty, sleeping each time around the loop.
302     // This copes with the possibility of a higher priority thread grabbing
303     // the message between the wakeup in put()&c and this thread actually
304     // starting.
305     while ( result && (0 == count) ) {
306         // must reset the sleep reason every time
307         self->set_sleep_reason( Cyg_Thread::TIMEOUT );
308         self->sleep();
309         get_threadq.enqueue( self );
310
311         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
312         
313         // Allow other threads to run
314         Cyg_Scheduler::reschedule();
315
316         CYG_ASSERTCLASS( this, "Bad this pointer");        
317
318         switch( self->get_wake_reason() )
319         {
320         case Cyg_Thread::TIMEOUT:
321             result = false;
322             CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
323             break;
324             
325         case Cyg_Thread::DESTRUCT:
326         case Cyg_Thread::BREAK:
327             result = false;
328             break;
329             
330         case Cyg_Thread::EXIT:            
331             self->exit();
332             break;
333
334         default:
335             break;
336         }
337     }
338
339     // clear the timer; if it actually fired, no worries.
340     self->clear_timer();
341
342     if ( result ) {
343
344         CYG_INSTRUMENT_MBOXT(GOT, this, count);
345     
346         ritem = itemqueue[ (count--, base++) ];
347         CYG_ASSERT( 0 <= count, "Count went -ve" );
348         CYG_ASSERT( size >= base, "Base overflow" );
349
350         if ( size <= base )
351             base = 0;
352
353 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
354         wakeup_waiter( put_threadq );
355 #endif
356     }
357
358     // Unlock the scheduler and maybe switch threads
359     Cyg_Scheduler::unlock();
360
361     CYG_ASSERTCLASS( this, "Bad this pointer");
362     CYG_REPORT_RETVAL( result );
363     return result;
364 }
365 #endif // CYGFUN_KERNEL_THREADS_TIMER
366
367 // -------------------------------------------------------------------------
368 // Try to get an item and return success.
369
370 template <class T, cyg_count32 QUEUE_SIZE>
371 CYG_MBOXT_INLINE cyg_bool
372 Cyg_Mboxt<T,QUEUE_SIZE>::tryget( T &ritem )
373 {
374     CYG_REPORT_FUNCTION();
375         
376     CYG_ASSERTCLASS( this, "Bad this pointer");
377     
378     // Prevent preemption
379     Cyg_Scheduler::lock();
380
381     CYG_INSTRUMENT_MBOXT(TRY, this, count);
382     
383     cyg_bool result = ( 0 < count );
384     // If the mboxt is not empty, grab an item and return it.
385     if ( result ) {
386         ritem = itemqueue[ (count--, base++) ];
387         CYG_ASSERT( 0 <= count, "Count went -ve" );
388         CYG_ASSERT( size >= base, "Base overflow" );
389         if ( size <= base )
390             base = 0;
391
392 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
393         wakeup_waiter( put_threadq );
394 #endif
395     }
396
397     // Unlock the scheduler and maybe switch threads
398     Cyg_Scheduler::unlock();
399     
400     return result;    
401 }
402
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 )
408 {
409     CYG_REPORT_FUNCTION();
410         
411     CYG_ASSERTCLASS( this, "Bad this pointer");
412     
413     // Prevent preemption
414     Cyg_Scheduler::lock();
415
416     CYG_INSTRUMENT_MBOXT(TRY, this, count);
417     
418     cyg_bool result = ( 0 < count );
419     // If the mboxt is not empty, grab an item and return it.
420     if ( result )
421         ritem = itemqueue[ base ];
422
423     // Unlock the scheduler and maybe switch threads
424     Cyg_Scheduler::unlock();
425     
426     return result;    
427 }
428
429 // -------------------------------------------------------------------------
430 // Put an item in the queue; wait if full.
431
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 )
436 {
437     CYG_REPORT_FUNCTION();
438     cyg_bool result = true;
439
440     Cyg_Thread *self = Cyg_Thread::self();
441
442     // Prevent preemption
443     Cyg_Scheduler::lock();
444
445     CYG_INSTRUMENT_MBOXT(PUT, this, count);
446     CYG_ASSERTCLASS( this, "Bad this pointer");
447
448     while ( result && (size == count) ) {
449         self->set_sleep_reason( Cyg_Thread::WAIT );
450         self->sleep();
451         put_threadq.enqueue( self );
452
453         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
454
455         // Allow other threads to run
456         Cyg_Scheduler::reschedule();
457
458         CYG_ASSERTCLASS( this, "Bad this pointer");        
459
460         switch( self->get_wake_reason() )
461         {
462         case Cyg_Thread::DESTRUCT:
463         case Cyg_Thread::BREAK:
464             result = false;
465             break;
466             
467         case Cyg_Thread::EXIT:            
468             self->exit();
469             break;
470
471         default:
472             break;
473         }
474     }
475
476     if ( result ) {
477         cyg_count32 in = base + (count++);
478         if ( size <= in )
479             in -= size;
480
481         CYG_ASSERT( size > in, "in overflow" );
482         CYG_ASSERT( 0 <= in, "in overflow" );
483         CYG_ASSERT( size >= count, "count overflow" );
484
485         itemqueue[ in ] = item;
486
487         wakeup_waiter( get_threadq );
488     }
489     CYG_ASSERTCLASS( this, "Bad this pointer");    
490     
491     // Unlock the scheduler and maybe switch threads
492     Cyg_Scheduler::unlock();
493     CYG_REPORT_RETVAL( result );
494     return result;
495 }
496
497 // -------------------------------------------------------------------------
498 // Put an item in the queue; wait if full, with an absolute timeout;
499 // return success.
500
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 )
505 {
506     CYG_REPORT_FUNCTION();
507     cyg_bool result = true;
508
509     Cyg_Thread *self = Cyg_Thread::self();
510
511     // Prevent preemption
512     Cyg_Scheduler::lock();
513
514     CYG_INSTRUMENT_MBOXT(PUT, this, count);
515     CYG_ASSERTCLASS( this, "Bad this pointer");
516
517     // Set the timer _once_ outside the loop.
518     self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT );
519
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.
523     
524     if( self->get_wake_reason() != Cyg_Thread::NONE )
525         result = false;
526     
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 );
534         self->sleep();
535         put_threadq.enqueue( self );
536
537         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
538
539         // Allow other threads to run
540         Cyg_Scheduler::reschedule();
541
542         CYG_ASSERTCLASS( this, "Bad this pointer");        
543
544         switch( self->get_wake_reason() )
545         {
546         case Cyg_Thread::TIMEOUT:
547             result = false;
548             CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
549             break;
550             
551         case Cyg_Thread::DESTRUCT:
552         case Cyg_Thread::BREAK:
553             result = false;
554             break;
555             
556         case Cyg_Thread::EXIT:            
557             self->exit();
558             break;
559
560         default:
561             break;
562         }
563     }
564
565     // clear the timer; if it actually fired, no worries.
566     self->clear_timer();
567
568     if ( result ) {
569         cyg_count32 in = base + (count++);
570         if ( size <= in )
571             in -= size;
572
573         CYG_ASSERT( size > in, "in overflow" );
574         CYG_ASSERT( 0 <= in, "in overflow" );
575         CYG_ASSERT( size >= count, "count overflow" );
576
577         itemqueue[ in ] = item;
578
579         wakeup_waiter( get_threadq );
580     }
581     CYG_ASSERTCLASS( this, "Bad this pointer");    
582     
583     // Unlock the scheduler and maybe switch threads
584     Cyg_Scheduler::unlock();
585     CYG_REPORT_RETVAL( result );
586     return result;
587 }
588 #endif // CYGFUN_KERNEL_THREADS_TIMER
589 #endif // CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
590
591 // -------------------------------------------------------------------------
592 // Try to put an item in the queue and return success; queue may be full.
593
594 template <class T, cyg_count32 QUEUE_SIZE>
595 CYG_MBOXT_INLINE cyg_bool
596 Cyg_Mboxt<T,QUEUE_SIZE>::tryput( const T item )
597 {
598     CYG_REPORT_FUNCTION();
599         
600     // Prevent preemption
601     Cyg_Scheduler::lock();
602
603     CYG_INSTRUMENT_MBOXT(PUT, this, count);
604     CYG_ASSERTCLASS( this, "Bad this pointer");
605
606     if ( size == count ) {
607         Cyg_Scheduler::unlock();        // unlock, maybe switch threads
608         return false;                   // the mboxt is full
609     }
610
611     cyg_count32 in = base + (count++);
612     if ( size <= in )
613         in -= size;
614
615     CYG_ASSERT( size > in, "in overflow" );
616     CYG_ASSERT( 0 <= in, "in overflow" );
617     CYG_ASSERT( size >= count, "count overflow" );
618
619     itemqueue[ in ] = item;
620
621     CYG_ASSERTCLASS( this, "Bad this pointer");    
622
623     wakeup_waiter( get_threadq );
624     
625     // Unlock the scheduler and maybe switch threads
626     Cyg_Scheduler::unlock();
627
628     return true;
629 }
630
631
632 // -------------------------------------------------------------------------
633 #endif // ifndef CYGONCE_KERNEL_MBOXT_INL
634 // EOF mboxt.inl