/*****************************************************************************

  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
  more contributor license agreements.  See the NOTICE file distributed
  with this work for additional information regarding copyright ownership.
  Accellera licenses this file to you under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with the
  License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  implied.  See the License for the specific language governing
  permissions and limitations under the License.

 *****************************************************************************/

/*****************************************************************************

  sc_simcontext_int.h -- For inline definitions of some utility functions.
                         DO NOT EXPORT THIS INCLUDE FILE. Include this file
                         after "sc_process_int.h" so that we can get the base
                         class right.

  Original Author: Stan Y. Liao, Synopsys, Inc.

  CHANGE LOG AT THE END OF THE FILE
 *****************************************************************************/

#ifndef SC_SIMCONTEXT_INT_H
#define SC_SIMCONTEXT_INT_H

#include "sysc/kernel/sc_simcontext.h"
#include "sysc/kernel/sc_runnable.h"
#include "sysc/kernel/sc_runnable_int.h"

// DEBUGGING MACROS:
//
// DEBUG_MSG(NAME,P,MSG)
//     MSG  = message to print
//     NAME = name that must match the process for the message to print, or
//            null if the message should be printed unconditionally.
//     P    = pointer to process message is for, or NULL in which case the
//            message will not print.
#if 0
#   define DEBUG_NAME ""
#   define DEBUG_MSG(NAME,P,MSG) \
    { \
        if ( P && ( (strlen(NAME)==0) || !strcmp(NAME,P->name())) ) \
          std::cout << sc_time_stamp() << ": " << P->name() << " ******** " \
                    << MSG << std::endl; \
    }
#else
#   define DEBUG_MSG(NAME,P,MSG) 
#endif

namespace sc_core {

// We use m_current_writer rather than m_curr_proc_info.process_handle to
// return the active process for sc_signal<T>::check_write since that lets
// us turn it off a library compile time, and only incur the overhead at
// the time of process switches rather than having to interrogate an
// additional switch every time a signal is written.

inline
void
sc_simcontext::set_curr_proc( sc_process_b* process_h )
{
    m_curr_proc_info.process_handle = process_h;
    m_curr_proc_info.kind           = process_h->proc_kind();
    m_current_writer = m_write_check ? process_h : (sc_object*)0;
}

inline
void
sc_simcontext::reset_curr_proc()
{
    m_curr_proc_info.process_handle = 0;
    m_curr_proc_info.kind           = SC_NO_PROC_;
    m_current_writer                = 0;
    sc_process_b::m_last_created_process_p = 0; 
}

inline
void
sc_simcontext::execute_method_next( sc_method_handle method_h )
{
    m_runnable->execute_method_next( method_h );
}

inline
void
sc_simcontext::execute_thread_next( sc_thread_handle thread_h )
{
    m_runnable->execute_thread_next( thread_h );
}

// +----------------------------------------------------------------------------
// |"sc_simcontext::preempt_with"
// | 
// | This method executes the supplied thread immediately, suspending the
// | caller. After executing the supplied thread the caller's execution will
// | be restored. It is used to allow a thread to immediately throw an 
// | exception, e.g., when the thread's kill_process() method was called.
// | There are three cases to consider:
// |   (1) The caller is a method, e.g., murder by method.
// |   (2) The caller is another thread instance, e.g., murder by thread.
// |   (3) The caller is this thread instance, e.g., suicide.
// |
// | Arguments:
// |     thread_h -> thread to be executed.
// +----------------------------------------------------------------------------
inline
void
sc_simcontext::preempt_with( sc_thread_handle thread_h )
{
    sc_thread_handle  active_p;    // active thread or null.
    sc_curr_proc_info caller_info; // process info for caller.

    // Determine the active process and take the thread to be run off the
    // run queue, if its there, since we will be explicitly causing its 
    // execution.

    active_p = DCAST<sc_thread_handle>(sc_get_current_process_b());
    if ( thread_h->next_runnable() != NULL )
	remove_runnable_thread( thread_h );

    // THE CALLER IS A METHOD:
    //
    //   (a) Set the current process information to our thread.
    //   (b) Invoke our thread directly by-passing the run queue.
    //   (c) Restore the process info to the caller.
    //   (d) Check to see if the calling method should throw an exception
    //       because of activity that occurred during the preemption.

    if ( active_p == NULL )
    {
        sc_method_handle                 method_p;   // active method.

	method_p = DCAST<sc_method_handle>(sc_get_current_process_b());
	caller_info = m_curr_proc_info;
        DEBUG_MSG( DEBUG_NAME, thread_h,
	           "preempting no active thread with this thread" );
	set_curr_proc( (sc_process_b*)thread_h );
	m_cor_pkg->yield( thread_h->m_cor_p );
	m_curr_proc_info = caller_info; 
	method_p->check_for_throws();
    }

    // CALLER IS A THREAD, BUT NOT THE THREAD TO BE RUN:
    //
    //   (a) Push the calling thread onto the front of the runnable queue
    //       so it be the first thread to be run after this thread.
    //   (b) Push the thread to be run onto the front of the runnable queue so 
    //       it will execute when we suspend the calling thread.
    //   (c) Suspend the active thread.

    else if ( active_p != thread_h )
    {
        DEBUG_MSG( DEBUG_NAME, thread_h,
	           "preempting active thread with this thread" );
        execute_thread_next( active_p );
	execute_thread_next( thread_h );
	active_p->suspend_me();
    }

    // CALLER IS THE THREAD TO BE RUN:
    //
    //   (a) Push the thread to be run onto the front of the runnable queue so 
    //       it will execute when we suspend the calling thread.
    //   (b) Suspend the active thread.

    else
    {
        DEBUG_MSG(DEBUG_NAME,thread_h,"self preemption of active thread");
	execute_thread_next( thread_h );
	active_p->suspend_me();
    }
}


inline
void
sc_simcontext::push_runnable_method( sc_method_handle method_h )
{
    m_runnable->push_back_method( method_h );
}

inline
void
sc_simcontext::push_runnable_method_front( sc_method_handle method_h )
{
    m_runnable->push_front_method( method_h );
}

inline
void
sc_simcontext::push_runnable_thread( sc_thread_handle thread_h )
{
    m_runnable->push_back_thread( thread_h );
}

inline
void
sc_simcontext::push_runnable_thread_front( sc_thread_handle thread_h )
{
    m_runnable->push_front_thread( thread_h );
}


inline
sc_method_handle
sc_simcontext::pop_runnable_method()
{
    sc_method_handle method_h = m_runnable->pop_method();
    if( method_h == 0 ) {
	reset_curr_proc();
	return 0;
    }
    set_curr_proc( (sc_process_b*)method_h );
    return method_h;
}

inline
sc_thread_handle
sc_simcontext::pop_runnable_thread()
{
    sc_thread_handle thread_h = m_runnable->pop_thread();
    if( thread_h == 0 ) {
	reset_curr_proc();
	return 0;
    }
    set_curr_proc( (sc_process_b*)thread_h );
    return thread_h;
}

inline
void
sc_simcontext::remove_runnable_method( sc_method_handle method_h )
{
    m_runnable->remove_method( method_h );
}

inline
void
sc_simcontext::remove_runnable_thread( sc_thread_handle thread_h )
{
    m_runnable->remove_thread( thread_h );
}

// ----------------------------------------------------------------------------

extern void sc_defunct_process_function( sc_module* );


} // namespace sc_core

#undef DEBUG_MSG
#undef DEBUG_NAME

// $Log: sc_simcontext_int.h,v $
// Revision 1.14  2011/08/29 18:04:32  acg
//  Philipp A. Hartmann: miscellaneous clean ups.
//
// Revision 1.13  2011/08/26 20:46:11  acg
//  Andy Goodrich: moved the modification log to the end of the file to
//  eliminate source line number skew when check-ins are done.
//
// Revision 1.12  2011/07/29 22:45:06  acg
//  Andy Goodrich: added invocation of sc_method_process::check_for_throws()
//  to the preempt_with() code to handle case where the preempting process
//  causes a throw on the invoking method process.
//
// Revision 1.11  2011/04/13 02:45:11  acg
//  Andy Goodrich: eliminated warning message that occurred if the DEBUG_MSG
//  macro was used.
//
// Revision 1.10  2011/04/11 22:05:48  acg
//  Andy Goodrich: use the DEBUG_NAME macro in DEBUG_MSG invocations.
//
// Revision 1.9  2011/04/10 22:12:32  acg
//  Andy Goodrich: adding debugging macros.
//
// Revision 1.8  2011/04/08 18:26:07  acg
//  Andy Goodrich: added execute_method_next() to handle method dispatch
//   for asynchronous notifications that occur outside the evaluation phase.
//
// Revision 1.7  2011/02/18 20:27:14  acg
//  Andy Goodrich: Updated Copyrights.
//
// Revision 1.6  2011/02/13 21:47:38  acg
//  Andy Goodrich: update copyright notice.
//
// Revision 1.5  2011/02/08 08:17:50  acg
//  Andy Goodrich: fixed bug in preempt_with() where I was resetting the
//  process context rather than saving and restoring it.
//
// Revision 1.4  2011/02/01 21:12:56  acg
//  Andy Goodrich: addition of preempt_with() method to allow immediate
//  execution of threads for throws.
//
// Revision 1.3  2011/01/25 20:50:37  acg
//  Andy Goodrich: changes for IEEE 1666 2011.
//
// Revision 1.2  2008/05/22 17:06:26  acg
//  Andy Goodrich: updated copyright notice to include 2008.
//
// Revision 1.1.1.1  2006/12/15 20:20:05  acg
// SystemC 2.3
//
// Revision 1.6  2006/05/26 20:33:16  acg
//   Andy Goodrich: changes required by additional platform compilers (i.e.,
//   Microsoft VC++, Sun Forte, HP aCC).
//
// Revision 1.5  2006/01/19 00:29:52  acg
// Andy Goodrich: Yet another implementation for signal write checking. This
// one uses an environment variable SC_SIGNAL_WRITE_CHECK, that when set to
// DISABLE will disable write checking on signals.
//
// Revision 1.4  2006/01/18 21:42:37  acg
// Andy Goodrich: Changes for check writer support.
//
// Revision 1.3  2006/01/13 18:44:30  acg
// Added $Log to record CVS changes into the source.

#endif
