]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/kernel/v2_0/include/mboxt.inl
unified MX27, MX25, MX37 trees
[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 // 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:     Mboxt template implementation
47 // Description: This file contains the implementations of the mboxt
48 //              template classes.
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/kernel.h>
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/mboxt.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_Mboxt<T,QUEUE_SIZE>::wakeup_waiter( Cyg_ThreadQueue &q )
73 {
74     if( !q.empty() ) {
75         // The queue is non-empty, so grab the next thread and wake it up.
76         Cyg_Thread *thread = q.dequeue();
77
78         CYG_ASSERTCLASS( thread, "Bad thread pointer");
79
80         thread->set_wake_reason( Cyg_Thread::DONE );
81         thread->wake();
82         CYG_INSTRUMENT_MBOXT(WAKE, this, thread);        
83     }
84 }
85
86 // -------------------------------------------------------------------------
87 // Constructor
88
89 template <class T, cyg_count32 QUEUE_SIZE>
90 Cyg_Mboxt<T,QUEUE_SIZE>::Cyg_Mboxt()
91 {
92     CYG_REPORT_FUNCTION();
93     base = 0;
94     count = 0;
95 }
96
97 // -------------------------------------------------------------------------
98 // Destructor
99
100 template <class T, cyg_count32 QUEUE_SIZE>
101 Cyg_Mboxt<T,QUEUE_SIZE>::~Cyg_Mboxt()
102 {
103     CYG_REPORT_FUNCTION();
104     CYG_ASSERT( 0 == count, "Deleting mboxt with messages");
105     CYG_ASSERT( get_threadq.empty(), "Deleting mboxt with threads waiting to get");
106 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
107     CYG_ASSERT( put_threadq.empty(), "Deleting mboxt with threads waiting to put");
108 #endif
109 }
110
111 // -------------------------------------------------------------------------
112 // debugging/assert function
113
114 #ifdef CYGDBG_USE_ASSERTS
115
116 template <class T, cyg_count32 QUEUE_SIZE>
117 cyg_bool 
118 Cyg_Mboxt<T,QUEUE_SIZE>::check_this(cyg_assert_class_zeal zeal) const
119 {
120     CYG_REPORT_FUNCTION();
121         
122     if ( Cyg_Thread::DESTRUCT == Cyg_Thread::self()->get_wake_reason() )
123         // then the whole thing is invalid, and we know it.
124         // so return OK, since this check should NOT make an error.
125         return true;
126
127     // check that we have a non-NULL pointer first
128     if( this == NULL ) return false;
129
130 #if 0 // thread queues do not have checking funcs.
131     if ( ! get_threadq.check_this( zeal ) ) return false;
132 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
133     if ( ! put_threadq.check_this( zeal ) ) return false;
134 #endif
135 #endif
136
137     switch( zeal )
138     {
139     case cyg_system_test:
140     case cyg_extreme:
141     case cyg_thorough:
142     case cyg_quick:
143     case cyg_trivial:
144         // plenty of scope for fencepost problems here
145         if ( size < count ) return false;
146         if ( size <= base ) return false;
147         if ( 0 > count ) return false;
148         if ( 0 > base  ) return false;
149       
150         // there was initially a test of the form
151         //    (0 < count && count < size) && ! threadqueue.empty()
152         // here - ie. there should only be people waiting if the Q is full
153         // or empty.  This is bogus, anyone else might run between a waiter
154         // being awoken, so there can be a 2nd waiter in the Q and a free
155         // slot (say) simultaneously.
156
157         // Further, we need 2 queues; imagine a 10-slot itemqueue with 25
158         // attempts to put to it, so 15 sleep.  10 other threads get,
159         // awakening 10 of the 15 put-sleepers.  Another one gets, and
160         // can't because there is no data there _yet_; it sleeps, and the
161         // 10 awakened threads cycle through the run queue, each putting,
162         // the first awakens the get-sleeper, which in turn awakens a
163         // further put-sleeper.
164
165         // This requirement for 2 queue only holds if Ngetters > 2 * Nslots
166         // or Nputters > 2 * Nslots; if these are both false, one queue
167         // will suffice.  This could be an optimisation for the future -
168         // wow, 4 bytes.
169
170     case cyg_none:
171     default:
172         break;
173     };
174
175     return true;
176 }
177
178 #endif
179
180
181 // -------------------------------------------------------------------------
182 // From here downwards, these are the major functions of the template; if
183 // being genuinely used as a template they should probably not be inlined.
184 // If being used to construct a specific class, with explicit functions,
185 // then they should be.  This is controlled by:
186
187 #ifdef CYGIMP_MBOXT_INLINE
188 #define CYG_MBOXT_INLINE inline
189 #else
190 #define CYG_MBOXT_INLINE
191 #endif
192
193 // -------------------------------------------------------------------------
194 // Get an item, or wait for one to arrive
195
196 template <class T, cyg_count32 QUEUE_SIZE>
197 CYG_MBOXT_INLINE cyg_bool
198 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem )
199 {
200     CYG_REPORT_FUNCTION();
201     cyg_bool result = true;
202         
203     Cyg_Thread *self = Cyg_Thread::self();
204     
205     // Prevent preemption
206     Cyg_Scheduler::lock();
207
208     CYG_ASSERTCLASS( this, "Bad this pointer");
209     
210     CYG_INSTRUMENT_MBOXT(GET, this, count);
211
212     // Loop while the mboxt is empty, sleeping each time around
213     // the loop. This copes with the possibility of a higher priority
214     // thread grabbing the message between the wakeup in unlock() and
215     // this thread actually starting.
216     
217     while( result && (0 == count) ) {
218         self->set_sleep_reason( Cyg_Thread::WAIT );
219         self->sleep();
220         get_threadq.enqueue( self );
221
222         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
223         
224         CYG_ASSERTCLASS( this, "Bad this pointer");        
225
226         // Allow other threads to run
227         Cyg_Scheduler::reschedule();
228
229         switch( self->get_wake_reason() )
230         {
231         case Cyg_Thread::DESTRUCT:
232         case Cyg_Thread::BREAK:
233             result = false;
234             break;
235             
236         case Cyg_Thread::EXIT:            
237             self->exit();
238             break;
239
240         default:
241             break;
242         }
243     }
244
245     if ( result ) {
246         CYG_INSTRUMENT_MBOXT(GOT, this, count);
247     
248         ritem = itemqueue[ (count--, base++) ];
249         CYG_ASSERT( 0 <= count, "Count went -ve" );
250         CYG_ASSERT( size >= base, "Base overflow" );
251
252         if ( size <= base )
253             base = 0;
254
255 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
256         wakeup_waiter( put_threadq );
257 #endif
258     }
259
260     CYG_ASSERTCLASS( this, "Bad this pointer");
261
262     // Unlock the scheduler and maybe switch threads
263     Cyg_Scheduler::unlock();
264
265     CYG_REPORT_RETVAL( result );
266     return result;
267 }
268
269
270 // -------------------------------------------------------------------------
271 // Try to get an item with an absolute timeout and return success.
272
273 #ifdef CYGFUN_KERNEL_THREADS_TIMER
274 template <class T, cyg_count32 QUEUE_SIZE>
275 CYG_MBOXT_INLINE cyg_bool
276 Cyg_Mboxt<T,QUEUE_SIZE>::get( T &ritem, cyg_tick_count abs_timeout )
277 {
278     CYG_REPORT_FUNCTION();
279     cyg_bool result = true;
280         
281     Cyg_Thread *self = Cyg_Thread::self();
282     
283     // Prevent preemption
284     Cyg_Scheduler::lock();
285
286     CYG_ASSERTCLASS( this, "Bad this pointer");
287     
288     CYG_INSTRUMENT_MBOXT(GET, this, count);
289
290     // Set the timer _once_ outside the loop.
291     self->set_timer( abs_timeout, Cyg_Thread::TIMEOUT  );
292
293     // If the timeout is in the past, the wake reason will have been
294     // set to something other than NONE already. Set the result false
295     // to force an immediate return.
296     
297     if( self->get_wake_reason() != Cyg_Thread::NONE )
298         result = false;
299                 
300     // Loop while the mboxt is empty, sleeping each time around the loop.
301     // This copes with the possibility of a higher priority thread grabbing
302     // the message between the wakeup in put()&c and this thread actually
303     // starting.
304     while ( result && (0 == count) ) {
305         // must reset the sleep reason every time
306         self->set_sleep_reason( Cyg_Thread::TIMEOUT );
307         self->sleep();
308         get_threadq.enqueue( self );
309
310         CYG_INSTRUMENT_MBOXT(WAIT, this, count);
311
312         // Allow other threads to run
313         Cyg_Scheduler::reschedule();
314
315         CYG_ASSERTCLASS( this, "Bad this pointer");        
316
317         switch( self->get_wake_reason() )
318         {
319         case Cyg_Thread::TIMEOUT:
320             result = false;
321             CYG_INSTRUMENT_MBOXT(TIMEOUT, this, count);
322             break;
323             
324         case Cyg_Thread::DESTRUCT:
325         case Cyg_Thread::BREAK:
326             result = false;
327             break;
328             
329         case Cyg_Thread::EXIT:            
330             self->exit();
331             break;
332
333         default:
334             break;
335         }
336     }
337
338     // clear the timer; if it actually fired, no worries.
339     self->clear_timer();
340
341     if ( result ) {
342
343         CYG_INSTRUMENT_MBOXT(GOT, this, count);
344     
345         ritem = itemqueue[ (count--, base++) ];
346         CYG_ASSERT( 0 <= count, "Count went -ve" );
347         CYG_ASSERT( size >= base, "Base overflow" );
348
349         if ( size <= base )
350             base = 0;
351
352 #ifdef CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT
353         wakeup_waiter( put_threadq );
354 #endif
355     }
356
357     CYG_ASSERTCLASS( this, "Bad this pointer");
358
359     // Unlock the scheduler and maybe switch threads
360     Cyg_Scheduler::unlock();
361
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     // Prevent preemption
377     Cyg_Scheduler::lock();
378
379     CYG_ASSERTCLASS( this, "Bad this pointer");
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     // Prevent preemption
412     Cyg_Scheduler::lock();
413
414     CYG_ASSERTCLASS( this, "Bad this pointer");
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