1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20/*******************************************************************************
21
22  sc_runnable_int.h -- For inline definitions of some utility functions.
23                       DO NOT EXPORT THIS INCLUDE FILE. Include this file
24                       after "sc_process_int.h" so that we can get the base
25                       class right.
26
27  Original Author: Bishnupriya Bhattacharya , Cadence Design, 28th July, 2003
28
29 CHANGE LOG AT THE END OF THE FILE
30 ******************************************************************************/
31
32#ifndef SC_RUNNABLE_INT_H
33#define SC_RUNNABLE_INT_H
34
35
36#include "sysc/kernel/sc_runnable.h"
37#include "sysc/kernel/sc_method_process.h"
38#include "sysc/kernel/sc_thread_process.h"
39
40// DEBUGGING MACROS:
41//
42// DEBUG_MSG(NAME,P,MSG)
43//     MSG  = message to print
44//     NAME = name that must match the process for the message to print, or
45//            null if the message should be printed unconditionally.
46//     P    = pointer to process message is for, or NULL in which case the
47//            message will not print.
48#if 0
49#   define DEBUG_NAME ""
50#   define DEBUG_MSG(NAME,P,MSG) \
51    { \
52        if ( P && ( (strlen(NAME)==0) || !strcmp(NAME,P->name())) ) \
53          std::cout << "**** " << sc_time_stamp() << " ("  \
54	            << sc_get_current_process_name() << "): " << MSG \
55		    << " - " << P->name() << std::endl; \
56    }
57#else
58#   define DEBUG_MSG(NAME,P,MSG)
59#endif
60
61namespace sc_core {
62
63// The values below are used to indicate when a queue is empty. A non-zero
64// non-legal pointer value is used for this so that a zero value in the
65// m_execute_p field of an sc_process_b instance can be used to indicate
66// that is has not been queued for run. (If we did not use a non-zero
67// queue empty indicator then a sc_process_b instance that was queued
68// twice in a row might end up on the queue twice if it were the first
69// one that was queued!)
70
71#define SC_NO_METHODS ((sc_method_handle)0xdb)
72#define SC_NO_THREADS ((sc_thread_handle)0xdb)
73
74
75//------------------------------------------------------------------------------
76//"sc_runnable::dump"
77//
78// This method dumps the contents of this object instance.
79//------------------------------------------------------------------------------
80inline void sc_runnable::dump() const
81{
82    // Dump the thread queues:
83
84    std::cout << "thread pop queue: " << std::endl;
85    for ( sc_thread_handle p = m_threads_pop; p != SC_NO_THREADS;
86          p = p->next_runnable() )
87    {
88        std::cout << "    " << p << std::endl;
89    }
90
91    std::cout << "thread push queue: " << std::endl;
92    for ( sc_thread_handle p = m_threads_push_head->next_runnable();
93          p != SC_NO_THREADS; p = p->next_runnable() )
94    {
95        std::cout << "    " << p << std::endl;
96    }
97}
98
99//------------------------------------------------------------------------------
100//"sc_runnable::execute_method_next"
101//
102// This method pushes the the supplied method to execute as the next process.
103// This is done by pushing it onto the front of the m_methods_pop.
104//     method_h -> method process to add to the queue.
105//------------------------------------------------------------------------------
106inline void sc_runnable::execute_method_next( sc_method_handle method_h )
107{
108    DEBUG_MSG(DEBUG_NAME,method_h,"pushing this method to execute next");
109    method_h->set_next_runnable( m_methods_pop );
110    m_methods_pop = method_h;
111}
112
113//------------------------------------------------------------------------------
114//"sc_runnable::execute_thread_next"
115//
116// This method pushes the the supplied thread to execute as the next process.
117// This is done by pushing it onto the front of the m_threads_pop.
118//     thread_h -> thread process to add to the queue.
119//------------------------------------------------------------------------------
120inline void sc_runnable::execute_thread_next( sc_thread_handle thread_h )
121{
122    DEBUG_MSG(DEBUG_NAME,thread_h,"pushing this thread to execute next");
123    thread_h->set_next_runnable( m_threads_pop );
124    m_threads_pop = thread_h;
125}
126
127//------------------------------------------------------------------------------
128//"sc_runnable::init"
129//
130// This method initializes this object instance. Note we allocate the queue
131// heads if necessary. This is done here rather than in the constructor for
132// this class to eliminate CTOR processing errors with gcc.
133//------------------------------------------------------------------------------
134inline void sc_runnable::init()
135{
136    m_methods_pop = SC_NO_METHODS;
137    if ( !m_methods_push_head )
138    {
139        m_methods_push_head = new sc_method_process("methods_push_head", true,
140	                                           (SC_ENTRY_FUNC)0, 0, 0);
141        m_methods_push_head->dont_initialize(true);
142	m_methods_push_head->detach();
143    }
144    m_methods_push_tail = m_methods_push_head;
145    m_methods_push_head->set_next_runnable(SC_NO_METHODS);
146
147    m_threads_pop = SC_NO_THREADS;
148    if ( !m_threads_push_head )
149    {
150        m_threads_push_head = new sc_thread_process("threads_push_head", true,
151	                                            (SC_ENTRY_FUNC)0, 0, 0);
152        m_threads_push_head->dont_initialize(true);
153	m_threads_push_head->detach();
154    }
155    m_threads_push_head->set_next_runnable(SC_NO_THREADS);
156    m_threads_push_tail = m_threads_push_head;
157}
158
159
160//------------------------------------------------------------------------------
161//"sc_runnable::is_empty"
162//
163// This method returns true if the push queue is empty, or false if not.
164//------------------------------------------------------------------------------
165inline bool sc_runnable::is_empty() const
166{
167    return m_methods_push_head->next_runnable() == SC_NO_METHODS &&
168           m_methods_pop == SC_NO_METHODS &&
169	   m_threads_push_head->next_runnable() == SC_NO_THREADS &&
170	   m_threads_pop == SC_NO_THREADS;
171}
172
173
174//------------------------------------------------------------------------------
175//"sc_runnable::is_initialized"
176//
177// This method returns true if the push queue is already initialized.
178//------------------------------------------------------------------------------
179inline bool sc_runnable::is_initialized() const
180{
181    return m_methods_push_head && m_threads_push_head;
182}
183
184
185//------------------------------------------------------------------------------
186//"sc_runnable::push_back_method"
187//
188// This method pushes the supplied method process onto the back of the queue of
189// runnable method processes.
190//     method_h -> method process to add to the queue.
191//------------------------------------------------------------------------------
192inline void sc_runnable::push_back_method( sc_method_handle method_h )
193{
194    // assert( method_h->next_runnable() == 0 ); // Can't queue twice.
195    DEBUG_MSG(DEBUG_NAME,method_h,"pushing back method");
196    method_h->set_next_runnable(SC_NO_METHODS);
197    m_methods_push_tail->set_next_runnable(method_h);
198    m_methods_push_tail = method_h;
199}
200
201
202//------------------------------------------------------------------------------
203//"sc_runnable::push_back_thread"
204//
205// This method pushes the supplied thread process onto the back of the queue of
206// runnable thread processes.
207//     thread_h -> thread process to add to the queue.
208//------------------------------------------------------------------------------
209inline void sc_runnable::push_back_thread( sc_thread_handle thread_h )
210{
211    // assert( thread_h->next_runnable() == 0 ); // Can't queue twice.
212    DEBUG_MSG(DEBUG_NAME,thread_h,"pushing back thread");
213    thread_h->set_next_runnable(SC_NO_THREADS);
214    m_threads_push_tail->set_next_runnable(thread_h);
215    m_threads_push_tail = thread_h;
216}
217
218
219//------------------------------------------------------------------------------
220//"sc_runnable::push_front_method"
221//
222// This method pushes the supplied method process onto the front of the queue of
223// runnable method processes. If the queue is empty the process is the tail
224// also.
225//     method_h -> method process to add to the queue.
226//------------------------------------------------------------------------------
227inline void sc_runnable::push_front_method( sc_method_handle method_h )
228{
229    // assert( method_h->next_runnable() == 0 ); // Can't queue twice.
230    DEBUG_MSG(DEBUG_NAME,method_h,"pushing front method");
231    method_h->set_next_runnable(m_methods_push_head->next_runnable());
232    if ( m_methods_push_tail == m_methods_push_head ) // Empty queue.
233    {
234        m_methods_push_tail->set_next_runnable(method_h);
235	m_methods_push_tail = method_h;
236    }
237    else                                               // Non-empty queue.
238    {
239	m_methods_push_head->set_next_runnable(method_h);
240    }
241}
242
243
244//------------------------------------------------------------------------------
245//"sc_runnable::push_front_thread"
246//
247// This method pushes the supplied thread process onto the front of the queue of
248// runnable thread processes. If the queue is empty the process is the tail
249// also.
250//     thread_h -> thread process to add to the queue.
251//------------------------------------------------------------------------------
252inline void sc_runnable::push_front_thread( sc_thread_handle thread_h )
253{
254    // assert( thread_h->next_runnable() == 0 ); // Can't queue twice.
255    DEBUG_MSG(DEBUG_NAME,thread_h,"pushing front thread");
256    thread_h->set_next_runnable(m_threads_push_head->next_runnable());
257    if ( m_threads_push_tail == m_threads_push_head ) // Empty queue.
258    {
259        m_threads_push_tail->set_next_runnable(thread_h);
260	m_threads_push_tail = thread_h;
261    }
262    else                                               // Non-empty queue.
263    {
264	m_threads_push_head->set_next_runnable(thread_h);
265    }
266}
267
268//------------------------------------------------------------------------------
269//"sc_runnable::pop_method"
270//
271// This method pops the next method process to be executed, or returns a null
272// if no method processes are available for execution.
273//------------------------------------------------------------------------------
274inline sc_method_handle sc_runnable::pop_method()
275{
276    sc_method_handle result_p;
277
278    result_p = m_methods_pop;
279    if ( result_p != SC_NO_METHODS )
280    {
281        m_methods_pop = result_p->next_runnable();
282        result_p->set_next_runnable(0);
283    }
284    else
285    {
286	result_p = 0;
287    }
288    DEBUG_MSG(DEBUG_NAME,result_p,"popping method");
289    return result_p;
290
291}
292
293//------------------------------------------------------------------------------
294//"sc_runnable::pop_thread"
295//
296// This method pops the next thread process to be executed, or returns a null
297// if no thread processes are available for execution.
298//------------------------------------------------------------------------------
299inline sc_thread_handle sc_runnable::pop_thread()
300{
301    sc_thread_handle result_p;
302
303    result_p = m_threads_pop;
304    if ( result_p != SC_NO_THREADS )
305    {
306        m_threads_pop = result_p->next_runnable();
307        result_p->set_next_runnable(0);
308    }
309    else
310    {
311	    result_p = 0;
312    }
313    DEBUG_MSG(DEBUG_NAME,result_p,"popping thread for execution");
314    return result_p;
315}
316
317
318//------------------------------------------------------------------------------
319//"sc_runnable::remove_method"
320//
321// This method removes the supplied method process from the push queue if it is
322// present. Note we clear the method's next pointer so that it may be queued
323// again.
324//     remove_p -> method process to remove from the run queue.
325//------------------------------------------------------------------------------
326inline void sc_runnable::remove_method( sc_method_handle remove_p )
327{
328    sc_method_handle now_p;     // Method now checking.
329    sc_method_handle prior_p;   // Method prior to now_p.
330
331    // Don't try to remove things if we have not been initialized.
332
333    if ( !is_initialized() ) return;
334
335    // Search the push queue:
336
337    prior_p = m_methods_push_head;
338    for ( now_p = m_methods_push_head; now_p!= SC_NO_METHODS;
339	    now_p = now_p->next_runnable() )
340    {
341        if ( remove_p == now_p )
342        {
343            prior_p->set_next_runnable( now_p->next_runnable() );
344            if (now_p == m_methods_push_tail) {
345                m_methods_push_tail = prior_p;
346            }
347            now_p->set_next_runnable(0);
348	    DEBUG_MSG(DEBUG_NAME,now_p,"removing method from push queue");
349            return;
350        }
351        prior_p = now_p;
352    }
353
354    // Search the pop queue:
355
356    prior_p = NULL;
357    for ( now_p = m_methods_pop; now_p != SC_NO_METHODS;
358	  now_p = now_p->next_runnable() )
359    {
360        if ( remove_p == now_p )
361        {
362	    if ( prior_p )
363		prior_p->set_next_runnable( now_p->next_runnable() );
364	    else
365	        m_methods_pop = now_p->next_runnable();
366            now_p->set_next_runnable(0);
367	    DEBUG_MSG(DEBUG_NAME,now_p,"removing method from pop queue");
368            return;
369        }
370        prior_p = now_p;
371    }
372}
373
374
375//------------------------------------------------------------------------------
376//"sc_runnable::remove_thread"
377//
378// This method removes the supplied thread process from the push or pop
379// queue if it is present. Note we clear the thread's next pointer so that it
380// may be queued again.
381//     remove_p -> thread process to remove from the run queue.
382//------------------------------------------------------------------------------
383inline void sc_runnable::remove_thread( sc_thread_handle remove_p )
384{
385    sc_thread_handle now_p;     // Thread now checking.
386    sc_thread_handle prior_p;   // Thread prior to now_p.
387
388    // Don't try to remove things if we have not been initialized.
389
390    if ( !is_initialized() ) return;
391
392    // Search the push queue:
393
394    prior_p = m_threads_push_head;
395    for ( now_p = m_threads_push_head; now_p != SC_NO_THREADS;
396	  now_p = now_p->next_runnable() )
397    {
398        if ( remove_p == now_p )
399        {
400            prior_p->set_next_runnable( now_p->next_runnable() );
401            if (now_p == m_threads_push_tail) {
402                m_threads_push_tail = prior_p;
403            }
404            now_p->set_next_runnable(0);
405	    DEBUG_MSG(DEBUG_NAME,now_p,"removing thread from push queue");
406            return;
407        }
408        prior_p = now_p;
409    }
410
411    // Search the pop queue:
412
413    prior_p = NULL;
414    for ( now_p = m_threads_pop; now_p != SC_NO_THREADS;
415	  now_p = now_p->next_runnable() )
416    {
417        if ( remove_p == now_p )
418        {
419	    if ( prior_p )
420		prior_p->set_next_runnable( now_p->next_runnable() );
421	    else
422	        m_threads_pop = now_p->next_runnable();
423            now_p->set_next_runnable(0);
424	    DEBUG_MSG(DEBUG_NAME,now_p,"removing thread from pop queue");
425            return;
426        }
427        prior_p = now_p;
428    }
429}
430
431//------------------------------------------------------------------------------
432//"sc_runnable::sc_runnable"
433//
434// This is the object instance constructor for this class.
435//------------------------------------------------------------------------------
436inline sc_runnable::sc_runnable() :
437   m_methods_push_head(0), m_methods_push_tail(0), m_methods_pop(SC_NO_METHODS),
438   m_threads_push_head(0), m_threads_push_tail(0), m_threads_pop(SC_NO_THREADS)
439{}
440
441//------------------------------------------------------------------------------
442//"sc_runnable::~sc_runnable"
443//
444// This is the object instance destructor for this class.
445//------------------------------------------------------------------------------
446inline sc_runnable::~sc_runnable()
447{
448    delete m_methods_push_head;
449    delete m_threads_push_head;
450}
451
452
453//------------------------------------------------------------------------------
454//"sc_runnable::toggle_methods"
455//
456// This method moves the methods push queue to the pop queue and zeros the push
457// queue. This will only be done if the pop queue is presently empty.
458//------------------------------------------------------------------------------
459inline void sc_runnable::toggle_methods()
460{
461    if ( m_methods_pop == SC_NO_METHODS )
462    {
463	m_methods_pop = m_methods_push_head->next_runnable();
464	m_methods_push_head->set_next_runnable(SC_NO_METHODS);
465	m_methods_push_tail = m_methods_push_head;
466    }
467}
468
469
470//------------------------------------------------------------------------------
471//"sc_runnable::toggle_threads"
472//
473// This method moves the threads push queue to the pop queue and zeros the push
474// queue. This will only be done if the pop queue is presently empty.
475//------------------------------------------------------------------------------
476inline void sc_runnable::toggle_threads()
477{
478    if ( m_threads_pop == SC_NO_THREADS )
479    {
480	m_threads_pop = m_threads_push_head->next_runnable();
481	m_threads_push_head->set_next_runnable(SC_NO_THREADS);
482	m_threads_push_tail = m_threads_push_head;
483    }
484}
485
486#undef SC_NO_METHODS
487#undef SC_NO_THREADS
488#undef DEBUG_MSG
489
490} // namespace sc_core
491
492
493/*******************************************************************************
494
495  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
496  changes you are making here.
497      Andy Goodrich, Forte Design Systems, 2 September 2003
498      Changed queue heads to instances to eliminate the checks for null heads.
499
500 ******************************************************************************/
501
502// $Log: sc_runnable_int.h,v $
503// Revision 1.19  2011/08/24 22:05:51  acg
504//  Torsten Maehne: initialization changes to remove warnings.
505//
506// Revision 1.18  2011/08/07 19:08:04  acg
507//  Andy Goodrich: moved logs to end of file so line number synching works
508//  better between versions.
509//
510// Revision 1.17  2011/04/13 02:45:11  acg
511//  Andy Goodrich: eliminated warning message that occurred if the DEBUG_MSG
512//  macro was used.
513//
514// Revision 1.16  2011/04/10 22:18:23  acg
515//  Andy Goodrich: debugging message clean up.
516//
517// Revision 1.15  2011/04/08 18:26:07  acg
518//  Andy Goodrich: added execute_method_next() to handle method dispatch
519//   for asynchronous notifications that occur outside the evaluation phase.
520//
521// Revision 1.14  2011/04/01 21:31:10  acg
522//  Andy Goodrich: turn off diagnostic messages by default.
523//
524// Revision 1.13  2011/04/01 21:30:02  acg
525//  Andy Goodrich: inserted conditional displays for queue manipulations.
526//
527// Revision 1.12  2011/03/30 00:01:34  acg
528//  Philip A. Hartmann: change break to return in remove_method() to short
529//  circuit the search the way remove_thread() works.
530//
531// Revision 1.11  2011/03/28 13:02:52  acg
532//  Andy Goodrich: Changes for disable() interactions.
533//
534// Revision 1.10  2011/03/06 15:58:17  acg
535//  Andy Goodrich: formatting changes.
536//
537// Revision 1.9  2011/02/18 20:27:14  acg
538//  Andy Goodrich: Updated Copyrights.
539//
540// Revision 1.8  2011/02/13 21:47:38  acg
541//  Andy Goodrich: update copyright notice.
542//
543// Revision 1.7  2011/02/02 06:37:03  acg
544//  Andy Goodrich: removed toggle() method since it is no longer used.
545//
546// Revision 1.6  2011/02/01 21:09:13  acg
547//  Andy Goodrich: addition of toggle_methods() and toggle_threads() calls.
548//
549// Revision 1.5  2011/01/25 20:50:37  acg
550//  Andy Goodrich: changes for IEEE 1666 2011.
551//
552// Revision 1.4  2010/07/22 20:02:33  acg
553//  Andy Goodrich: bug fixes.
554//
555// Revision 1.3  2009/02/28 00:26:58  acg
556//  Andy Goodrich: changed boost name space to sc_boost to allow use with
557//  full boost library applications.
558//
559// Revision 1.2  2008/05/22 17:06:26  acg
560//  Andy Goodrich: updated copyright notice to include 2008.
561//
562// Revision 1.1.1.1  2006/12/15 20:20:05  acg
563// SystemC 2.3
564//
565// Revision 1.4  2006/04/20 17:08:17  acg
566//  Andy Goodrich: 3.0 style process changes.
567//
568// Revision 1.3  2006/01/13 18:44:30  acg
569// Added $Log to record CVS changes into the source.
570//
571
572#endif // SC_RUNNABLE_INT_H
573
574// Taf!
575