mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
258 lines
7.5 KiB
C++
258 lines
7.5 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file thread.cxx
|
|
* @author drose
|
|
* @date 2002-08-08
|
|
*/
|
|
|
|
#include "thread.h"
|
|
#include "mainThread.h"
|
|
#include "externalThread.h"
|
|
#include "config_pipeline.h"
|
|
#include "mutexDebug.h"
|
|
#include "conditionVarDebug.h"
|
|
#include "conditionVarFullDebug.h"
|
|
|
|
Thread *Thread::_main_thread;
|
|
Thread *Thread::_external_thread;
|
|
TypeHandle Thread::_type_handle;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
Thread::
|
|
Thread(const std::string &name, const std::string &sync_name) :
|
|
Namable(name),
|
|
_sync_name(sync_name),
|
|
_impl(this)
|
|
{
|
|
_started = false;
|
|
_pstats_index = -1;
|
|
_python_index = -1;
|
|
_pstats_callback = nullptr;
|
|
_pipeline_stage = 0;
|
|
_joinable = false;
|
|
_current_task = nullptr;
|
|
|
|
#ifdef DEBUG_THREADS
|
|
_blocked_on_mutex = nullptr;
|
|
_waiting_on_cvar = nullptr;
|
|
_waiting_on_cvar_full = nullptr;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
Thread::
|
|
~Thread() {
|
|
#ifdef DEBUG_THREADS
|
|
nassertv(_blocked_on_mutex == nullptr &&
|
|
_waiting_on_cvar == nullptr &&
|
|
_waiting_on_cvar_full == nullptr);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Returns a new Panda Thread object associated with the current thread (which
|
|
* has been created externally). This can be used to bind a unique Panda
|
|
* Thread object with an external thread, such as a new Python thread.
|
|
*
|
|
* It is particularly useful to bind a Panda Thread object to an external
|
|
* thread for the purposes of PStats monitoring. Without this call, each
|
|
* external thread will be assigned the same global ExternalThread object,
|
|
* which means they will all appear in the same PStats graph.
|
|
*
|
|
* It is the caller's responsibility to save the returned Thread pointer for
|
|
* the lifetime of the external thread. It is an error for the Thread pointer
|
|
* to destruct while the external thread is still in the system.
|
|
*
|
|
* It is also an error to call this method from the main thread, or twice
|
|
* within a given thread, unless it is given the same name each time (in which
|
|
* case the same pointer will be returned each time).
|
|
*/
|
|
PT(Thread) Thread::
|
|
bind_thread(const std::string &name, const std::string &sync_name) {
|
|
Thread *current_thread = get_current_thread();
|
|
if (current_thread != get_external_thread()) {
|
|
// This thread already has an associated thread.
|
|
nassertr(current_thread->get_name() == name &&
|
|
current_thread->get_sync_name() == sync_name, current_thread);
|
|
return current_thread;
|
|
}
|
|
|
|
PT(Thread) thread = new ExternalThread(name, sync_name);
|
|
ThreadImpl::bind_thread(thread);
|
|
return thread;
|
|
}
|
|
|
|
/**
|
|
* Specifies the Pipeline stage number associated with this thread. The
|
|
* default stage is 0 if no stage is specified otherwise.
|
|
*
|
|
* This must be a value in the range [0 .. pipeline->get_num_stages() - 1].
|
|
* It specifies the values that this thread observes for all pipelined data.
|
|
* Typically, an application thread will leave this at 0, but a render thread
|
|
* may set it to 1 or 2 (to operate on the previous frame's data, or the
|
|
* second previous frame's data).
|
|
*/
|
|
void Thread::
|
|
set_pipeline_stage(int pipeline_stage) {
|
|
#ifdef THREADED_PIPELINE
|
|
_pipeline_stage = pipeline_stage;
|
|
#else
|
|
if (pipeline_stage != 0) {
|
|
pipeline_cat.warning()
|
|
<< "Requested pipeline stage " << pipeline_stage
|
|
<< " but multithreaded render pipelines not enabled in build.\n";
|
|
}
|
|
_pipeline_stage = 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void Thread::
|
|
output(std::ostream &out) const {
|
|
out << get_type() << " " << get_name();
|
|
}
|
|
|
|
/**
|
|
* Writes a description of the mutex or condition variable that this thread is
|
|
* blocked on. Writes nothing if there is no blocker, or if we are not in
|
|
* DEBUG_THREADS mode.
|
|
*/
|
|
void Thread::
|
|
output_blocker(std::ostream &out) const {
|
|
#ifdef DEBUG_THREADS
|
|
if (_blocked_on_mutex != nullptr) {
|
|
_blocked_on_mutex->output_with_holder(out);
|
|
} else if (_waiting_on_cvar != nullptr) {
|
|
out << *_waiting_on_cvar;
|
|
} else if (_waiting_on_cvar_full != nullptr) {
|
|
out << *_waiting_on_cvar_full;
|
|
}
|
|
#endif // DEBUG_THREADS
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void Thread::
|
|
write_status(std::ostream &out) {
|
|
#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
|
|
ThreadImpl::write_status(out);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Starts the thread executing. It is only valid to call this once.
|
|
*
|
|
* The thread will begin executing its thread_main() function, and will
|
|
* terminate when thread_main() returns.
|
|
*
|
|
* priority is intended as a hint to the relative importance of this thread.
|
|
* This may be ignored by the thread implementation.
|
|
*
|
|
* joinable should be set true if you intend to call join() to wait for the
|
|
* thread to terminate, or false if you don't care and you will never call
|
|
* join(). Note that the reference count on the Thread object is incremented
|
|
* while the thread itself is running, so if you just want to fire and forget
|
|
* a thread, you may pass joinable = false, and never store the Thread object.
|
|
* It will automatically destruct itself when it finishes.
|
|
*
|
|
* The return value is true if the thread is successfully started, false
|
|
* otherwise.
|
|
*/
|
|
bool Thread::
|
|
start(ThreadPriority priority, bool joinable) {
|
|
nassertr(!_started, false);
|
|
|
|
if (!support_threads) {
|
|
thread_cat->warning()
|
|
<< *this << " could not be started: support-threads is false.\n";
|
|
return false;
|
|
}
|
|
|
|
_joinable = joinable;
|
|
_started = _impl.start(priority, joinable);
|
|
|
|
if (!_started) {
|
|
thread_cat->warning()
|
|
<< *this << " could not be started!\n";
|
|
}
|
|
|
|
return _started;
|
|
}
|
|
|
|
/**
|
|
* Creates the Thread object that represents the main thread.
|
|
*/
|
|
void Thread::
|
|
init_main_thread() {
|
|
// There is a chance of mutual recursion at startup. The count variable
|
|
// here attempts to protect against that.
|
|
static int count = 0;
|
|
++count;
|
|
if (count == 1 && _main_thread == nullptr) {
|
|
init_memory_hook();
|
|
_main_thread = new MainThread;
|
|
_main_thread->ref();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates the Thread object that represents all of the external threads.
|
|
*/
|
|
void Thread::
|
|
init_external_thread() {
|
|
if (_external_thread == nullptr) {
|
|
init_memory_hook();
|
|
_external_thread = new ExternalThread;
|
|
_external_thread->ref();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Since this class is just an interface definition, there is no need to have
|
|
* a destructor. However, we must have one anyway to stop gcc's annoying
|
|
* warning.
|
|
*/
|
|
Thread::PStatsCallback::
|
|
~PStatsCallback() {
|
|
}
|
|
|
|
/**
|
|
* Called when the thread is deactivated (swapped for another running thread).
|
|
* This is intended to provide a callback hook for PStats to assign time to
|
|
* individual threads properly, particularly in the SIMPLE_THREADS case.
|
|
*/
|
|
void Thread::PStatsCallback::
|
|
deactivate_hook(Thread *) {
|
|
}
|
|
|
|
/**
|
|
* Called when the thread is activated (resumes execution). This is intended
|
|
* to provide a callback hook for PStats to assign time to individual threads
|
|
* properly, particularly in the SIMPLE_THREADS case.
|
|
*/
|
|
void Thread::PStatsCallback::
|
|
activate_hook(Thread *) {
|
|
}
|