//------------------------------------------------------------------------------
//"sc_method_process::throw_reset"
//
// This virtual method is invoked to "throw" a reset. 
//
// If the reset is synchronous this is a no-op, except for triggering the
// reset event if it is present.
//
// If the reset is asynchronous we:
//   (a) cancel any dynamic waits 
//   (b) if it is the active process actually throw a reset exception.
//   (c) if it was not the active process and does not have a static
//       sensitivity emit an error if corner cases are to be considered
//       errors.
//
// Notes:
//   (1) If the process had a reset event it will have been triggered in 
//       sc_process_b::semantics()
//
// Arguments:
//   async = true if this is an asynchronous reset.
//------------------------------------------------------------------------------
void sc_method_process::throw_reset( bool async )
{
    // IF THE PROCESS IS CURRENTLY UNWINDING OR IS ALREADY A ZOMBIE
    // IGNORE THE RESET:

    if ( m_unwinding )
    {
        SC_REPORT_WARNING( SC_ID_PROCESS_ALREADY_UNWINDING_, name() );
        return;
    }

    if ( m_state & ps_bit_zombie )
        return;

    // Set the throw status and if its an asynchronous reset throw an
    // exception:

    m_throw_status = async ? THROW_ASYNC_RESET : THROW_SYNC_RESET;
    if ( async )
    {
        remove_dynamic_events();
	if ( sc_get_current_process_b() == this )
	{
	    DEBUG_MSG(DEBUG_NAME,this,"throw_reset: throwing exception");
	    m_throw_status = THROW_ASYNC_RESET;
	    throw sc_unwind_exception( this, true );
	}
	else 
	{
	    DEBUG_MSG(DEBUG_NAME,this,
	              "throw_reset: queueing this method for execution");
	    simcontext()->preempt_with(this);
	}
    }
}
//------------------------------------------------------------------------------
//"sc_process_b::reset_process"
//
// This inline method changes the reset state of this object instance and
// conditionally its descendants.
//
// Notes:
//   (1) It is called for sync_reset_on() and sync_reset_off(). It is not used
//       for signal sensitive resets, though all reset flow ends up in
//       reset_changed().
//
// Arguments:
//     rt = source of the reset:
//            * reset_asynchronous     - sc_process_handle::reset()
//            * reset_synchronous_off  - sc_process_handle::sync_reset_off()
//            * reset_synchronous_on   - sc_process_handle::sync_reset_on()
//     descendants = indication of how to process descendants.
//------------------------------------------------------------------------------
void sc_process_b::reset_process( reset_type rt,
                                  sc_descendant_inclusion_info descendants )
{

    // PROCESS THIS OBJECT INSTANCE'S DESCENDANTS IF REQUESTED TO:

    if ( descendants == SC_INCLUDE_DESCENDANTS )
    {
        const std::vector<sc_object*> children = get_child_objects();
        int                           child_n  = children.size();

        for ( int child_i = 0; child_i < child_n; child_i++ )
        {
            sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]);
            if ( child_p ) child_p->reset_process(rt, descendants);
        }
    }

    // PROCESS THIS OBJECT INSTANCE:

    switch (rt)
    {
    // One-shot asynchronous reset: remove dynamic sensitivity and throw:
    //
    // If this is an sc_method only throw if it is active.

    case reset_asynchronous:
        if ( sc_get_status() != SC_RUNNING )
        {
            report_error(SC_ID_RESET_PROCESS_WHILE_NOT_RUNNING_);
        }
        else
        {
            remove_dynamic_events();
            throw_reset(true);
        }
        break;

    // Turn on sticky synchronous reset: use standard reset mechanism.

    case reset_synchronous_on:
        if ( m_sticky_reset == false )
        {
            m_sticky_reset = true;
            reset_changed( false, true );
        }
        break;

    // Turn off sticky synchronous reset: use standard reset mechanism.

    default:
        if ( m_sticky_reset == true )
        {
            m_sticky_reset = false;
            reset_changed( false, false );
        }
        break;
    }
}
//------------------------------------------------------------------------------
//"sc_process_b::disconnect_process"
//
// This method removes this object instance from use. It will be called by
// the kill_process() methods of classes derived from it. This object instance
// will be removed from any data structures it resides, other than existence.
//------------------------------------------------------------------------------
void sc_process_b::disconnect_process()
{
    int               mon_n;      // monitor queue size.
    sc_thread_handle  thread_h;   // This process as a thread.

    // IF THIS OBJECT IS PINING FOR THE FJORDS WE ARE DONE:

    if ( m_state & ps_bit_zombie ) return;

    // IF THIS IS A THREAD SIGNAL ANY MONITORS WAITING FOR IT TO EXIT:

    switch ( m_process_kind )
    {
    case SC_THREAD_PROC_:
    case SC_CTHREAD_PROC_:
        thread_h = SCAST<sc_thread_handle>(this);
        mon_n = thread_h->m_monitor_q.size();
        if ( mon_n )
        {
            for ( int mon_i = 0; mon_i < mon_n; mon_i++ )
            {
                thread_h->m_monitor_q[mon_i]->signal( thread_h,
                                                      sc_process_monitor::spm_exit);
            }
        }
        break;
    default:
        break;
    }

    // REMOVE EVENT WAITS, AND REMOVE THE PROCESS FROM ITS SC_RESET:

    remove_dynamic_events();
    remove_static_events();

    for ( size_t rst_i = 0; rst_i < m_resets.size(); rst_i++ )
    {
        m_resets[rst_i]->remove_process( this );
    }
    m_resets.resize(0);


    // FIRE THE TERMINATION EVENT, MARK AS TERMINATED, AND DECREMENT THE COUNT:
    //
    // (1) We wait to set the process kind until after doing the removals
    //     above.
    // (2) Decrementing the reference count will result in actual object
    //     deletion if we hit zero.

    m_state = ps_bit_zombie;
    if ( m_term_event_p ) m_term_event_p->notify();
    reference_decrement();
}
//------------------------------------------------------------------------------
//"sc_method_process::throw_user"
//
// This virtual method is invoked when a user exception is to be thrown.
// If requested it will also throw the exception to the children of this 
// object instance. Since this is a method no throw will occur for this
// object instance. The children will be awakened from youngest child to
// eldest.
//     helper_p    -> object to use to throw the exception.
//     descendants =  indicator of whether this process' children should also
//                    be suspended
//------------------------------------------------------------------------------
void sc_method_process::throw_user( const sc_throw_it_helper& helper,
    sc_descendant_inclusion_info descendants )
{     

    // IF THE SIMULATION IS NOT ACTUALLY RUNNING THIS IS AN ERROR:

    if (  sc_get_status() != SC_RUNNING )
    {
        report_error( SC_ID_THROW_IT_WHILE_NOT_RUNNING_ );
    }

    // IF NEEDED PROPOGATE THE THROW REQUEST THROUGH OUR DESCENDANTS:

    if ( descendants == SC_INCLUDE_DESCENDANTS )
    {
        const std::vector<sc_object*> children = get_child_objects();
        int                           child_n  = children.size();

        for ( int child_i = 0; child_i < child_n; child_i++ )
        {
            sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]);
            if ( child_p ) 
	    {
	        DEBUG_MSG(DEBUG_NAME,child_p,"about to throw user on");
	        child_p->throw_user(helper, descendants);
	    }
        }
    }

#if 0 // shouldn't we throw, if we're currently running?

    if ( sc_get_current_process_b() == (sc_process_b*)this )
    {
        remove_dynamic_events();
        m_throw_status = THROW_USER;
        if ( m_throw_helper_p != 0 ) delete m_throw_helper_p;
        m_throw_helper_p = helper.clone();
        m_throw_helper_p->throw_it();
    }

    // throw_it HAS NO EFFECT ON A METHOD, ISSUE A WARNING:

    else

#endif
   {
        SC_REPORT_WARNING( SC_ID_THROW_IT_IGNORED_, name() );
   }


}
//------------------------------------------------------------------------------
//"sc_method_process::resume_process"
//
// This method resumes the execution of this process, and if requested, its
// descendants. If the process was suspended and has a resumption pending it 
// will be dispatched in the next delta cycle. Otherwise the state will be
// adjusted to indicate it is no longer suspended, but no immediate execution
// will occur.
//------------------------------------------------------------------------------
void sc_method_process::resume_process(
    sc_descendant_inclusion_info descendants )
{

    // IF NEEDED PROPOGATE THE RESUME REQUEST THROUGH OUR DESCENDANTS:

    if ( descendants == SC_INCLUDE_DESCENDANTS )
    {
        const std::vector<sc_object*>& children = get_child_objects();
        int                            child_n  = children.size();

        for ( int child_i = 0; child_i < child_n; child_i++ )
        {
            sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]);
            if ( child_p ) child_p->resume_process(descendants);
        }
    }


    // BY DEFAULT THE CORNER CASE IS AN ERROR:

    if ( !sc_allow_process_control_corners && (m_state & ps_bit_disabled) &&
         (m_state & ps_bit_suspended) )
    {
	m_state = m_state & ~ps_bit_suspended;
        report_error( SC_ID_PROCESS_CONTROL_CORNER_CASE_, 
	              "call to resume() on a disabled suspended method");
    }

    // CLEAR THE SUSPENDED BIT:

    m_state = m_state & ~ps_bit_suspended;

    // RESUME OBJECT INSTANCE:
    //
    // If this is not a self-resume and the method is ready to run then
    // put it on the runnable queue.

    if ( m_state & ps_bit_ready_to_run )
    {
	m_state = m_state & ~ps_bit_ready_to_run;
	if ( next_runnable() == 0 && 
	   ( sc_get_current_process_b() != DCAST<sc_process_b*>(this) ) )
        {
	    simcontext()->push_runnable_method(this);
	    remove_dynamic_events();  
	}
    }
}
//------------------------------------------------------------------------------
//"sc_method_process::trigger_dynamic"
//
// This method sets up a dynamic trigger on an event.
//
// Notes:
//   (1) This method is identical to sc_thread_process::trigger_dynamic(), 
//       but they cannot be combined as sc_process_b::trigger_dynamic() 
//       because the signatures things like sc_event::remove_dynamic()
//       have different overloads for sc_method_process* and sc_thread_process*.
//       So if you change code here you'll also need to change it in 
//       sc_thread_process.cpp.
//
// Result is true if this process should be removed from the event's list,
// false if not.
//
// If the triggering process is the same process, the trigger is
// ignored as well, unless SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS
// is defined.
//------------------------------------------------------------------------------
bool sc_method_process::trigger_dynamic( sc_event* e )
{
    // No time outs yet, and keep gcc happy.

    m_timed_out = false;

    // Escape cases:
    //   (a) If this method issued the notify() don't schedule it for
    //       execution, but leave the sensitivity in place.
    //   (b) If this method is already runnable can't trigger an event.

#if ! defined( SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS )
    if( SC_UNLIKELY_( sc_get_current_process_b() == this ) )
    {
        report_immediate_self_notification();
        return false;
    }
#endif // SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS

    if( is_runnable() ) 
        return true;

    // If a process is disabled then we ignore any events, leaving them enabled:
    //
    // But if this is a time out event we need to remove both it and the
    // event that was being waited for.

    if ( m_state & ps_bit_disabled )
    {
        if ( e == m_timeout_event_p )
	{
	    remove_dynamic_events( true );  
	    return true;
	}
	else
	{
	    return false;
	}
    }


    // Process based on the event type and current process state:
    //
    // Every case needs to set 'rc' and continue on to the end of
    // this method to allow suspend processing to work correctly.

    switch( m_trigger_type ) 
    {
      case EVENT: 
	m_event_p = 0;
	m_trigger_type = STATIC;
	break;

      case AND_LIST:
        -- m_event_count;
	if ( m_event_count == 0 )
	{
	    m_event_list_p->auto_delete();
	    m_event_list_p = 0;
	    m_trigger_type = STATIC;
	}
	else
	{
	    return true;
	}
	break;

      case OR_LIST:
	m_event_list_p->remove_dynamic( this, e );
	m_event_list_p->auto_delete();
	m_event_list_p = 0;
	m_trigger_type = STATIC;
	break;

      case TIMEOUT: 
	m_trigger_type = STATIC;
	break;

      case EVENT_TIMEOUT: 
        if ( e == m_timeout_event_p )
	{
	    m_timed_out = true;
	    m_event_p->remove_dynamic( this );
	    m_event_p = 0;
	    m_trigger_type = STATIC;
	}
	else
	{
	    m_timeout_event_p->cancel();
	    m_timeout_event_p->reset();
	    m_event_p = 0;
	    m_trigger_type = STATIC;
	}
	break;

      case OR_LIST_TIMEOUT:
        if ( e == m_timeout_event_p )
	{
            m_timed_out = true;
            m_event_list_p->remove_dynamic( this, e ); 
            m_event_list_p->auto_delete();
            m_event_list_p = 0; 
            m_trigger_type = STATIC;
	}

	else
	{
            m_timeout_event_p->cancel();
            m_timeout_event_p->reset();
	    m_event_list_p->remove_dynamic( this, e ); 
	    m_event_list_p->auto_delete();
	    m_event_list_p = 0; 
	    m_trigger_type = STATIC;
	}
	break;
      
      case AND_LIST_TIMEOUT:
        if ( e == m_timeout_event_p )
	{
            m_timed_out = true;
            m_event_list_p->remove_dynamic( this, e ); 
            m_event_list_p->auto_delete();
            m_event_list_p = 0; 
            m_trigger_type = STATIC;
	}

	else
	{
	    -- m_event_count;
	    if ( m_event_count == 0 )
	    {
		m_timeout_event_p->cancel();
		m_timeout_event_p->reset();
		// no need to remove_dynamic
		m_event_list_p->auto_delete();
		m_event_list_p = 0; 
		m_trigger_type = STATIC;
	    }
	    else
	    {
	        return true;
	    }
	}
	break;

      case STATIC: {
        // we should never get here, but throw_it() can make it happen.
	SC_REPORT_WARNING(SC_ID_NOT_EXPECTING_DYNAMIC_EVENT_NOTIFY_, name());
        return true;
      }
    }

    // If we get here then the method has satisfied its next_trigger, if its 
    // suspended mark its state as ready to run. If its not suspended then push
    // it onto the runnable queue.

    if ( (m_state & ps_bit_suspended) )
    {
	m_state = m_state | ps_bit_ready_to_run;
    }
    else
    {
        simcontext()->push_runnable_method(this);
    }

    return true;
}