// Filename: thread.I
// Created by:  drose (08Aug02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//     Function: Thread::Constructor
//       Access: Protected
//  Description: Creates a new Thread object, but does not
//               immediately start executing it.  This gives the
//               caller a chance to store it in a PT(Thread) object,
//               if desired, before the thread gets a chance to
//               terminate and destruct itself.
//
//               Call start() to begin thread execution.
//
//               The name should be unique for each thread (though
//               this is not enforced, and not strictly required).
//               The sync_name can be shared between multiple
//               different threads; threads that run synchronously
//               with each other should be given the same sync_name,
//               for the benefit of PStats.
////////////////////////////////////////////////////////////////////
INLINE Thread::
Thread(const string &name, const string &sync_name) : 
  _name(name), 
  _sync_name(sync_name), 
  _impl(this) 
{
  _started = false;
  _pstats_index = -1;
  _pipeline_stage = 0;

#ifdef DEBUG_THREADS
  _blocked_on_mutex = NULL;
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::Copy Constructor
//       Access: Private
//  Description: Do not attempt to copy threads.
////////////////////////////////////////////////////////////////////
INLINE Thread::
Thread(const Thread &copy) : _impl(this) {
  nassertv(false);
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::Copy Assignment Operator
//       Access: Private
//  Description: Do not attempt to copy threads.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
operator = (const Thread &copy) {
  nassertv(false);
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_name
//       Access: Published
//  Description: Returns the name of the thread.  Each thread should
//               probably have a unique name, but this is not enforced
//               (and not strictly required).
////////////////////////////////////////////////////////////////////
INLINE const string &Thread::
get_name() const {
  return _name;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_sync_name
//       Access: Published
//  Description: Returns the sync name of the thread.  This name
//               collects threads into "sync groups", which are
//               expected to run synchronously.  This is mainly used
//               for the benefit of PStats; threads with the same sync
//               name can be ticked all at once via the thread_tick()
//               call.
////////////////////////////////////////////////////////////////////
INLINE const string &Thread::
get_sync_name() const {
  return _sync_name;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_pstats_index
//       Access: Published
//  Description: Returns the PStats index associated with this thread,
//               or -1 if no index has yet been associated with this
//               thread.  This is used internally by the PStatClient;
//               you should not need to call this directly.
////////////////////////////////////////////////////////////////////
INLINE int Thread::
get_pstats_index() const {
  return _pstats_index;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::set_pstats_index
//       Access: Published
//  Description: Stores a PStats index to be associated with this
//               thread.  This is used internally by the PStatClient;
//               you should not need to call this directly.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
set_pstats_index(int pstats_index) {
  _pstats_index = pstats_index;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_pipeline_stage
//       Access: Published
//  Description: Returns the Pipeline stage number associated with
//               this thread.  The default stage is 0 if no stage is
//               specified otherwise.  See set_pipeline_stage().
////////////////////////////////////////////////////////////////////
INLINE int Thread::
get_pipeline_stage() const {
  return _pipeline_stage;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::set_min_pipeline_stage
//       Access: Published
//  Description: Sets this thread's pipeline stage number to at least
//               the indicated value, unless it is already larger.
//               See set_pipeline_stage().
////////////////////////////////////////////////////////////////////
INLINE void Thread::
set_min_pipeline_stage(int min_pipeline_stage) {
  set_pipeline_stage(max(_pipeline_stage, min_pipeline_stage));
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_main_thread
//       Access: Published, Static
//  Description: Returns a pointer to the "main" Thread object--this
//               is the Thread that started the whole process.
////////////////////////////////////////////////////////////////////
INLINE Thread *Thread::
get_main_thread() {
  if (_main_thread == (Thread *)NULL) {
    init_main_thread();
  }
  return _main_thread;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_external_thread
//       Access: Published, Static
//  Description: Returns a pointer to the "external" Thread
//               object--this is a special Thread object that
//               corresponds to any thread spawned outside of Panda's
//               threading interface.  Note that multiple different
//               threads may share this same pointer.
////////////////////////////////////////////////////////////////////
INLINE Thread *Thread::
get_external_thread() {
  if (_external_thread == (Thread *)NULL) {
    init_external_thread();
  }
  return _external_thread;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_current_thread
//       Access: Published, Static
//  Description: Returns a pointer to the currently-executing Thread
//               object.  If this is called from the main thread, this
//               will return the same value as get_main_thread().
//
//               This will always return some valid Thread pointer.
//               It will never return NULL, even if the current thread
//               was spawned outside of Panda's threading system,
//               although all non-Panda threads will return the exact
//               same Thread pointer.
////////////////////////////////////////////////////////////////////
INLINE Thread *Thread::
get_current_thread() {
  TAU_PROFILE("Thread *Thread::get_current_thread()", " ", TAU_USER);
#ifndef HAVE_THREADS
  return get_main_thread();
#else  // HAVE_THREADS
  Thread *thread = ThreadImpl::get_current_thread();
  if (thread == (Thread *)NULL) {
    return Thread::get_external_thread();
  }
  return thread;
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::get_current_pipeline_stage
//       Access: Published, Static
//  Description: Returns the integer pipeline stage associated with
//               the current thread.  This is the same thing as
//               get_current_thread()->get_pipeline_stage(), but it
//               may be faster to retrieve in some contexts.
////////////////////////////////////////////////////////////////////
INLINE int Thread::
get_current_pipeline_stage() {
  TAU_PROFILE("int Thread::get_current_pipeline_stage()", " ", TAU_USER);
#ifndef THREADED_PIPELINE
  // Without threaded pipelining, the result is always 0.
  return 0;
#else
  return get_current_thread()->get_pipeline_stage();
#endif  // !THREADED_PIPELINE
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::is_threading_supported
//       Access: Published, Static
//  Description: Returns true if a real threading library is available
//               that supports threads, or false if no threading
//               library is available (and Thread::start() will always
//               fail).
////////////////////////////////////////////////////////////////////
INLINE bool Thread::
is_threading_supported() {
  if (!support_threads) {
    return false;
  }
  return ThreadImpl::is_threading_supported();
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::sleep
//       Access: Published, Static
//  Description: Suspends the current thread for at least the
//               indicated amount of time.  It might be suspended for
//               longer.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
sleep(double seconds) {
  TAU_PROFILE("void Thread::sleep(double)", " ", TAU_USER);
  ThreadImpl::sleep(seconds);
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::is_started
//       Access: Public
//  Description: Returns true if the thread has been started, false if
//               it has not, or if join() has already been called.
////////////////////////////////////////////////////////////////////
INLINE bool Thread::
is_started() const {
  return _started;
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::interrupt
//       Access: Public
//  Description: Sends an interrupt message to the thread.  This will
//               interrupt any blocking-type system calls the thread
//               may be waiting on, such as I/O, so that the thread
//               may continue some other processing.  The specific
//               behavior is implementation dependent.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
interrupt() {
  nassertv(_started);
  _impl.interrupt();
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::join
//       Access: Public
//  Description: Blocks the calling process until the thread
//               terminates.  If the thread has already terminated,
//               this returns immediately.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
join() {
  TAU_PROFILE("void Thread::join()", " ", TAU_USER);
  if (_started) {
    _impl.join();
    _started = false;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: Thread::prepare_for_exit
//       Access: Public
//  Description: Should be called by the main thread just before
//               exiting the program, this blocks until any remaining
//               thread cleanup has finished.
////////////////////////////////////////////////////////////////////
INLINE void Thread::
prepare_for_exit() {
  ThreadImpl::prepare_for_exit();
}

INLINE ostream &
operator << (ostream &out, const Thread &thread) {
  thread.output(out);
  return out;
}
