123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
- #include "threading/thread.h"
- #include "threading/mutex_auto_lock.h"
- #include "log.h"
- #include "porting.h"
- // for setName
- #if defined(__linux__)
- #include <sys/prctl.h>
- #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
- #include <pthread_np.h>
- #elif defined(__NetBSD__)
- #include <sched.h>
- #elif defined(_MSC_VER)
- struct THREADNAME_INFO {
- DWORD dwType; // Must be 0x1000
- LPCSTR szName; // Pointer to name (in user addr space)
- DWORD dwThreadID; // Thread ID (-1=caller thread)
- DWORD dwFlags; // Reserved for future use, must be zero
- };
- #endif
- // for bindToProcessor
- #if __FreeBSD_version >= 702106
- typedef cpuset_t cpu_set_t;
- #elif defined(__sun) || defined(sun)
- #include <sys/types.h>
- #include <sys/processor.h>
- #include <sys/procset.h>
- #elif defined(_AIX)
- #include <sys/processor.h>
- #include <sys/thread.h>
- #elif defined(__APPLE__)
- #include <mach/mach_init.h>
- #include <mach/thread_act.h>
- #endif
- Thread::Thread(const std::string &name) :
- m_name(name),
- m_request_stop(false),
- m_running(false)
- {
- #ifdef _AIX
- m_kernel_thread_id = -1;
- #endif
- }
- Thread::~Thread()
- {
- // kill the thread if running
- if (!m_running) {
- wait();
- } else {
- m_running = false;
- #if defined(_WIN32)
- // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method
- TerminateThread((HANDLE) m_thread_obj->native_handle(), 0);
- CloseHandle((HANDLE) m_thread_obj->native_handle());
- #else
- // We need to pthread_kill instead on Android since NDKv5's pthread
- // implementation is incomplete.
- # ifdef __ANDROID__
- pthread_kill(getThreadHandle(), SIGKILL);
- # else
- pthread_cancel(getThreadHandle());
- # endif
- wait();
- #endif
- }
- // Make sure start finished mutex is unlocked before it's destroyed
- if (m_start_finished_mutex.try_lock())
- m_start_finished_mutex.unlock();
- }
- bool Thread::start()
- {
- MutexAutoLock lock(m_mutex);
- if (m_running)
- return false;
- m_request_stop = false;
- // The mutex may already be locked if the thread is being restarted
- m_start_finished_mutex.try_lock();
- try {
- m_thread_obj = new std::thread(threadProc, this);
- } catch (const std::system_error &e) {
- return false;
- }
- while (!m_running)
- sleep_ms(1);
- // Allow spawned thread to continue
- m_start_finished_mutex.unlock();
- m_joinable = true;
- return true;
- }
- bool Thread::stop()
- {
- m_request_stop = true;
- return true;
- }
- bool Thread::wait()
- {
- MutexAutoLock lock(m_mutex);
- if (!m_joinable)
- return false;
- m_thread_obj->join();
- delete m_thread_obj;
- m_thread_obj = nullptr;
- assert(m_running == false);
- m_joinable = false;
- return true;
- }
- bool Thread::getReturnValue(void **ret)
- {
- if (m_running)
- return false;
- *ret = m_retval;
- return true;
- }
- void Thread::threadProc(Thread *thr)
- {
- #ifdef _AIX
- thr->m_kernel_thread_id = thread_self();
- #endif
- thr->setName(thr->m_name);
- g_logger.registerThread(thr->m_name);
- thr->m_running = true;
- // Wait for the thread that started this one to finish initializing the
- // thread handle so that getThreadId/getThreadHandle will work.
- thr->m_start_finished_mutex.lock();
- thr->m_retval = thr->run();
- thr->m_running = false;
- // Unlock m_start_finished_mutex to prevent data race condition on Windows.
- // On Windows with VS2017 build TerminateThread is called and this mutex is not
- // released. We try to unlock it from caller thread and it's refused by system.
- thr->m_start_finished_mutex.unlock();
- g_logger.deregisterThread();
- }
- void Thread::setName(const std::string &name)
- {
- #if defined(__linux__)
- // It would be cleaner to do this with pthread_setname_np,
- // which was added to glibc in version 2.12, but some major
- // distributions are still runing 2.11 and previous versions.
- prctl(PR_SET_NAME, name.c_str());
- #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
- pthread_set_name_np(pthread_self(), name.c_str());
- #elif defined(__NetBSD__)
- pthread_setname_np(pthread_self(), "%s", const_cast<char*>(name.c_str()));
- #elif defined(__APPLE__)
- pthread_setname_np(name.c_str());
- #elif defined(__HAIKU__)
- rename_thread(find_thread(NULL), name.c_str());
- #elif defined(_MSC_VER)
- // Windows itself doesn't support thread names,
- // but the MSVC debugger does...
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = name.c_str();
- info.dwThreadID = -1;
- info.dwFlags = 0;
- __try {
- RaiseException(0x406D1388, 0,
- sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
- } __except (EXCEPTION_CONTINUE_EXECUTION) {
- }
- #elif defined(_WIN32) || defined(__GNU__)
- // These platforms are known to not support thread names.
- // Silently ignore the request.
- #else
- #warning "Unrecognized platform, thread names will not be available."
- #endif
- }
- unsigned int Thread::getNumberOfProcessors()
- {
- return std::thread::hardware_concurrency();
- }
- bool Thread::bindToProcessor(unsigned int proc_number)
- {
- #if defined(__ANDROID__)
- return false;
- #elif _MSC_VER
- return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);
- #elif __MINGW32__
- return SetThreadAffinityMask(pthread_gethandle(getThreadHandle()), 1 << proc_number);
- #elif __FreeBSD_version >= 702106 || defined(__linux__) || defined(__DragonFly__)
- cpu_set_t cpuset;
- CPU_ZERO(&cpuset);
- CPU_SET(proc_number, &cpuset);
- return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
- #elif defined(__NetBSD__)
- cpuset_t *cpuset = cpuset_create();
- if (cpuset == NULL)
- return false;
- int r = pthread_setaffinity_np(getThreadHandle(), cpuset_size(cpuset), cpuset);
- cpuset_destroy(cpuset);
- return r == 0;
- #elif defined(__sun) || defined(sun)
- return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
- #elif defined(_AIX)
- return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
- #elif defined(__hpux) || defined(hpux)
- pthread_spu_t answer;
- return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
- &answer, proc_number, getThreadHandle()) == 0;
- #elif defined(__APPLE__)
- struct thread_affinity_policy tapol;
- thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
- tapol.affinity_tag = proc_number + 1;
- return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
- (thread_policy_t)&tapol,
- THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
- #else
- return false;
- #endif
- }
- bool Thread::setPriority(int prio)
- {
- #ifdef _MSC_VER
- return SetThreadPriority(getThreadHandle(), prio);
- #elif __MINGW32__
- return SetThreadPriority(pthread_gethandle(getThreadHandle()), prio);
- #else
- struct sched_param sparam;
- int policy;
- if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
- return false;
- int min = sched_get_priority_min(policy);
- int max = sched_get_priority_max(policy);
- sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
- return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
- #endif
- }
|