// $Id: Task.cpp 95761 2012-05-15 18:23:04Z johnnyw $

#include "ace/Task.h"
#include "ace/Module.h"

#if !defined (__ACE_INLINE__)
#include "ace/Task.inl"
#endif /* __ACE_INLINE__ */


ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_Task_Base::ACE_Task_Base (ACE_Thread_Manager *thr_man)
  : thr_count_ (0),
    thr_mgr_ (thr_man),
    flags_ (0),
    grp_id_ (-1)
    ,last_thread_id_ (0)
{
}

ACE_Task_Base::~ACE_Task_Base (void)
{
}

/// Default ACE_Task service routine
int
ACE_Task_Base::svc (void)
{
  ACE_TRACE ("ACE_Task_Base::svc");
  return 0;
}

/// Default ACE_Task open routine
int
ACE_Task_Base::open (void *)
{
  ACE_TRACE ("ACE_Task_Base::open");
  return 0;
}

/// Default ACE_Task close routine
int
ACE_Task_Base::close (u_long)
{
  ACE_TRACE ("ACE_Task_Base::close");
  return 0;
}

/// Forward the call to close() so that existing applications don't
/// break.
int
ACE_Task_Base::module_closed (void)
{
  return this->close (1);
}

/// Default ACE_Task put routine.
int
ACE_Task_Base::put (ACE_Message_Block *, ACE_Time_Value *)
{
  ACE_TRACE ("ACE_Task_Base::put");
  return 0;
}

/// Wait for all threads running in a task to exit.
int
ACE_Task_Base::wait (void)
{
  ACE_TRACE ("ACE_Task_Base::wait");

  // If we don't have a thread manager, we probably were never
  // activated.
  if (this->thr_mgr () != 0)
    return this->thr_mgr ()->wait_task (this);
  else
    return 0;
}

/// Suspend a task.
int
ACE_Task_Base::suspend (void)
{
  ACE_TRACE ("ACE_Task_Base::suspend");
  ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1));
  if (this->thr_count_ > 0)
    return this->thr_mgr_->suspend_task (this);

  return 0;
}

/// Resume a suspended task.
int
ACE_Task_Base::resume (void)
{
  ACE_TRACE ("ACE_Task_Base::resume");
  ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1));
  if (this->thr_count_ > 0)
    return this->thr_mgr_->resume_task (this);

  return 0;
}

int
ACE_Task_Base::activate (long flags,
                         int n_threads,
                         int force_active,
                         long priority,
                         int grp_id,
                         ACE_Task_Base *task,
                         ACE_hthread_t thread_handles[],
                         void *stack[],
                         size_t stack_size[],
                         ACE_thread_t thread_ids[],
                         const char* thr_name[])
{
  ACE_TRACE ("ACE_Task_Base::activate");

#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0)
  ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->lock_, -1);

  // If the task passed in is zero, we will use <this>
  if (task == 0)
    task = this;

  if (this->thr_count_ > 0 && force_active == 0)
    return 1; // Already active.
  else
    {
      if (this->thr_count_ > 0 && this->grp_id_ != -1)
        // If we're joining an existing group of threads then make
        // sure to use its group id.
        grp_id = this->grp_id_;
      this->thr_count_ += n_threads;
    }

  // Use the ACE_Thread_Manager singleton if we're running as an
  // active object and the caller didn't supply us with a
  // Thread_Manager.
  if (this->thr_mgr_ == 0)
# if defined (ACE_THREAD_MANAGER_LACKS_STATICS)
    this->thr_mgr_ = ACE_THREAD_MANAGER_SINGLETON::instance ();
# else /* ! ACE_THREAD_MANAGER_LACKS_STATICS */
    this->thr_mgr_ = ACE_Thread_Manager::instance ();
# endif /* ACE_THREAD_MANAGER_LACKS_STATICS */

  int grp_spawned = -1;
  if (thread_ids == 0)
    // Thread Ids were not specified
    grp_spawned =
      this->thr_mgr_->spawn_n (n_threads,
                               &ACE_Task_Base::svc_run,
                               (void *) this,
                               flags,
                               priority,
                               grp_id,
                               task,
                               thread_handles,
                               stack,
                               stack_size,
                               thr_name);
  else
    // thread names were specified
    grp_spawned =
      this->thr_mgr_->spawn_n (thread_ids,
                               n_threads,
                               &ACE_Task_Base::svc_run,
                               (void *) this,
                               flags,
                               priority,
                               grp_id,
                               stack,
                               stack_size,
                               thread_handles,
                               task,
                               thr_name);
  if (grp_spawned == -1)
    {
      // If spawn_n fails, restore original thread count.
      this->thr_count_ -= n_threads;
      return -1;
    }

  if (this->grp_id_ == -1)
    this->grp_id_ = grp_spawned;

#if defined(ACE_TANDEM_T1248_PTHREADS)
  ACE_OS::memcpy( &this->last_thread_id_, '\0', sizeof(this->last_thread_id_));
#else
  this->last_thread_id_ = 0;    // Reset to prevent inadvertant match on ID
#endif /* defined (ACE_TANDEM_T1248_PTHREADS) */

  return 0;

#else
  {
    // Keep the compiler from complaining.
    ACE_UNUSED_ARG (flags);
    ACE_UNUSED_ARG (n_threads);
    ACE_UNUSED_ARG (force_active);
    ACE_UNUSED_ARG (priority);
    ACE_UNUSED_ARG (grp_id);
    ACE_UNUSED_ARG (task);
    ACE_UNUSED_ARG (thread_handles);
    ACE_UNUSED_ARG (stack);
    ACE_UNUSED_ARG (stack_size);
    ACE_UNUSED_ARG (thread_ids);
    ACE_UNUSED_ARG (thr_name);
    ACE_NOTSUP_RETURN (-1);
  }
#endif /* ACE_MT_SAFE */
}

void
ACE_Task_Base::cleanup (void *object, void *)
{
  ACE_Task_Base *t = (ACE_Task_Base *) object;

  // The thread count must be decremented first in case the <close>
  // hook does something crazy like "delete this".
  {
    ACE_MT (ACE_GUARD (ACE_Thread_Mutex, ace_mon, t->lock_));
    t->thr_count_--;
    if (0 == t->thr_count_)
      t->last_thread_id_ = ACE_Thread::self ();
  }

  // @@ Is it possible to pass in the exit status somehow?
  t->close ();
  // t is undefined here. close() could have deleted it.
}


#if defined (ACE_HAS_SIG_C_FUNC)
extern "C" void
ACE_Task_Base_cleanup (void *object, void *)
{
  ACE_Task_Base::cleanup (object, 0);
}
#endif /* ACE_HAS_SIG_C_FUNC */

ACE_THR_FUNC_RETURN
ACE_Task_Base::svc_run (void *args)
{
  ACE_TRACE ("ACE_Task_Base::svc_run");

  ACE_Task_Base *t = (ACE_Task_Base *) args;

  // Register ourself with our <Thread_Manager>'s thread exit hook
  // mechanism so that our close() hook will be sure to get invoked
  // when this thread exits.

#if defined ACE_HAS_SIG_C_FUNC
  t->thr_mgr ()->at_exit (t, ACE_Task_Base_cleanup, 0);
#else
  t->thr_mgr ()->at_exit (t, ACE_Task_Base::cleanup, 0);
#endif /* ACE_HAS_SIG_C_FUNC */

  // Call the Task's svc() hook method.
  int const svc_status = t->svc ();
  ACE_THR_FUNC_RETURN status;
#if defined (ACE_HAS_INTEGRAL_TYPE_THR_FUNC_RETURN)
  // Reinterpret case between integral types is not mentioned in the C++ spec
  status = static_cast<ACE_THR_FUNC_RETURN> (svc_status);
#else
  status = reinterpret_cast<ACE_THR_FUNC_RETURN> (svc_status);
#endif /* ACE_HAS_INTEGRAL_TYPE_THR_FUNC_RETURN */

// If we changed this zero change the other if in OS.cpp Thread_Adapter::invoke
#if 1
  // Call the <Task->close> hook.
  ACE_Thread_Manager *thr_mgr_ptr = t->thr_mgr ();

  // This calls the Task->close () hook.
  t->cleanup (t, 0);

  // This prevents a second invocation of the cleanup code
  // (called later by <ACE_Thread_Manager::exit>.
  thr_mgr_ptr->at_exit (t, 0, 0);
#endif
  return status;
}

ACE_END_VERSIONED_NAMESPACE_DECL
