| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- /**
- * 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 threadWin32Impl.cxx
- * @author drose
- * @date 2006-02-07
- */
- #include "threadWin32Impl.h"
- #include "selectThreadImpl.h"
- #ifdef THREAD_WIN32_IMPL
- #include "thread.h"
- #include "pointerTo.h"
- #include "config_pipeline.h"
- #include <windows.h>
- static thread_local Thread *_current_thread = nullptr;
- static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT;
- #if _WIN32_WINNT < 0x0601
- // Requires Windows 7.
- static DWORD (*EnableThreadProfiling)(HANDLE, DWORD, DWORD64, HANDLE *) = nullptr;
- static DWORD (*DisableThreadProfiling)(HANDLE) = nullptr;
- static DWORD (*ReadThreadProfilingData)(HANDLE, DWORD, PPERFORMANCE_DATA data) = nullptr;
- static bool init_thread_profiling() {
- static bool inited = false;
- if (!inited) {
- HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
- EnableThreadProfiling = (decltype(EnableThreadProfiling))GetProcAddress(kernel32, "EnableThreadProfiling");
- DisableThreadProfiling = (decltype(DisableThreadProfiling))GetProcAddress(kernel32, "DisableThreadProfiling");
- ReadThreadProfilingData = (decltype(ReadThreadProfilingData))GetProcAddress(kernel32, "ReadThreadProfilingData");
- inited = true;
- }
- return (EnableThreadProfiling && DisableThreadProfiling && ReadThreadProfilingData);
- }
- #else
- static bool init_thread_profiling() {
- return true;
- }
- #endif
- /**
- * Called by get_current_thread() if the current thread pointer is null; checks
- * whether it might be the main thread.
- * Note that adding noinline speeds up this call *significantly*, don't remove!
- */
- static __declspec(noinline) Thread *
- init_current_thread() {
- Thread *thread = _current_thread;
- if (!_main_thread_known.test_and_set(std::memory_order_relaxed)) {
- // Assume that we must be in the main thread, since this method must be
- // called before the first thread is spawned.
- thread = Thread::get_main_thread();
- _current_thread = thread;
- }
- // If this assertion triggers, you are making Panda calls from a thread
- // that has not first been registered using Thread::bind_thread().
- nassertr(thread != nullptr, nullptr);
- return thread;
- }
- /**
- *
- */
- ThreadWin32Impl::
- ~ThreadWin32Impl() {
- if (thread_cat->is_debug()) {
- thread_cat.debug() << "Deleting thread " << _parent_obj->get_name() << "\n";
- }
- CloseHandle(_thread);
- }
- /**
- * Called for the main thread only, which has been already started, to fill in
- * the values appropriate to that thread.
- */
- void ThreadWin32Impl::
- setup_main_thread() {
- _status = S_running;
- }
- /**
- *
- */
- bool ThreadWin32Impl::
- start(ThreadPriority priority, bool joinable) {
- _mutex.lock();
- if (thread_cat->is_debug()) {
- thread_cat.debug() << "Starting " << *_parent_obj << "\n";
- }
- nassertd(_status == S_new && _thread == 0) {
- _mutex.unlock();
- return false;
- }
- _joinable = joinable;
- _status = S_start_called;
- // Increment the parent object's reference count first. The thread will
- // eventually decrement it when it terminates.
- _parent_obj->ref();
- _thread =
- CreateThread(nullptr, 0, &root_func, (void *)this, 0, &_thread_id);
- if (_thread_id == 0) {
- // Oops, we couldn't start the thread. Be sure to decrement the reference
- // count we incremented above, and return false to indicate failure.
- unref_delete(_parent_obj);
- _mutex.unlock();
- return false;
- }
- // Thread was successfully started. Set the priority as specified.
- switch (priority) {
- case TP_low:
- SetThreadPriority(_thread, THREAD_PRIORITY_BELOW_NORMAL);
- break;
- case TP_high:
- SetThreadPriority(_thread, THREAD_PRIORITY_ABOVE_NORMAL);
- break;
- case TP_urgent:
- SetThreadPriority(_thread, THREAD_PRIORITY_HIGHEST);
- break;
- case TP_normal:
- default:
- SetThreadPriority(_thread, THREAD_PRIORITY_NORMAL);
- break;
- }
- _mutex.unlock();
- return true;
- }
- /**
- * Blocks the calling process until the thread terminates. If the thread has
- * already terminated, this returns immediately.
- */
- void ThreadWin32Impl::
- join() {
- _mutex.lock();
- nassertd(_joinable && _status != S_new) {
- _mutex.unlock();
- return;
- }
- while (_status != S_finished) {
- _cv.wait();
- }
- _mutex.unlock();
- }
- /**
- *
- */
- std::string ThreadWin32Impl::
- get_unique_id() const {
- std::ostringstream strm;
- strm << GetCurrentProcessId() << "." << _thread_id;
- return strm.str();
- }
- /**
- *
- */
- Thread *ThreadWin32Impl::
- get_current_thread() {
- Thread *thread = _current_thread;
- return (thread != nullptr) ? thread : init_current_thread();
- }
- /**
- * Associates the indicated Thread object with the currently-executing thread.
- * You should not call this directly; use Thread::bind_thread() instead.
- */
- void ThreadWin32Impl::
- bind_thread(Thread *thread) {
- if (_current_thread == nullptr && thread == Thread::get_main_thread()) {
- _main_thread_known.test_and_set(std::memory_order_relaxed);
- }
- _current_thread = thread;
- }
- /**
- * Returns the number of context switches that occurred on the current thread.
- * The first number is the total number of context switches reported by the OS,
- * and the second number is the number of involuntary context switches (ie. the
- * thread was scheduled out by the OS), if known, otherwise zero.
- * Returns true if context switch information was available, false otherwise.
- */
- bool ThreadWin32Impl::
- get_context_switches(size_t &total, size_t &involuntary) {
- Thread *thread = get_current_thread();
- ThreadWin32Impl *self = &thread->_impl;
- if (!self->_profiling && init_thread_profiling()) {
- DWORD result = EnableThreadProfiling(GetCurrentThread(), THREAD_PROFILING_FLAG_DISPATCH, 0, &self->_profiling);
- if (result != ERROR_SUCCESS) {
- self->_profiling = 0;
- return false;
- }
- }
- PERFORMANCE_DATA data = {sizeof(PERFORMANCE_DATA), PERFORMANCE_DATA_VERSION};
- if (ReadThreadProfilingData(self->_profiling, READ_THREAD_PROFILING_FLAG_DISPATCHING, &data) == ERROR_SUCCESS) {
- total = data.ContextSwitchCount;
- involuntary = 0;
- return true;
- }
- return false;
- }
- /**
- * The entry point of each thread.
- */
- DWORD ThreadWin32Impl::
- root_func(LPVOID data) {
- TAU_REGISTER_THREAD();
- {
- // TAU_PROFILE("void ThreadWin32Impl::root_func()", " ", TAU_USER);
- ThreadWin32Impl *self = (ThreadWin32Impl *)data;
- _current_thread = self->_parent_obj;
- {
- self->_mutex.lock();
- nassertd(self->_status == S_start_called) {
- self->_mutex.unlock();
- return 1;
- }
- self->_status = S_running;
- self->_cv.notify();
- self->_mutex.unlock();
- }
- self->_parent_obj->thread_main();
- if (thread_cat->is_debug()) {
- thread_cat.debug()
- << "Terminating thread " << self->_parent_obj->get_name()
- << ", count = " << self->_parent_obj->get_ref_count() << "\n";
- }
- {
- self->_mutex.lock();
- nassertd(self->_status == S_running) {
- self->_mutex.unlock();
- return 1;
- }
- self->_status = S_finished;
- self->_cv.notify();
- self->_mutex.unlock();
- }
- if (self->_profiling != 0) {
- DisableThreadProfiling(self->_profiling);
- self->_profiling = 0;
- }
- // Now drop the parent object reference that we grabbed in start(). This
- // might delete the parent object, and in turn, delete the ThreadWin32Impl
- // object.
- unref_delete(self->_parent_obj);
- }
- return 0;
- }
- #endif // THREAD_WIN32_IMPL
|