mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 11:35:53 +00:00
* Replace custom types with C++11 <cstdint> types * Fix sqlite integer intos and uses * Fix String implicit integer constructors * Escape the enclosing namespace in sqdb * Replace contract checks with assert * Make many header files compile independently * Remove the dependence on beast_core.h in many places * Remove unused or obsolete classes and functions * Remove unused or obsolete macros * Remove unused network functions and files * Remove unused or obsolete classes: - ChildProcess - HighResolutionTimer - InterProcessLock - Throw - TrackedMutex - UntrackedMutex - XmlDocument - XmlElement
614 lines
15 KiB
C++
614 lines
15 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
|
|
Portions of this file are from JUCE.
|
|
Copyright (c) 2013 - Raw Material Software Ltd.
|
|
Please visit http://www.juce.com
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include "../Thread.h"
|
|
#include "../../smart_ptr/SharedObject.h"
|
|
#include "../../smart_ptr/SharedPtr.h"
|
|
#include "../../../modules/beast_core/time/Time.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace beast {
|
|
|
|
Thread::Thread (const String& threadName_)
|
|
: threadName (threadName_),
|
|
threadHandle (nullptr),
|
|
threadId (0),
|
|
threadPriority (5),
|
|
affinityMask (0),
|
|
shouldExit (false)
|
|
{
|
|
}
|
|
|
|
Thread::~Thread()
|
|
{
|
|
/* If your thread class's destructor has been called without first stopping the thread, that
|
|
means that this partially destructed object is still performing some work - and that's
|
|
probably a Bad Thing!
|
|
|
|
To avoid this type of nastiness, always make sure you call stopThread() before or during
|
|
your subclass's destructor.
|
|
*/
|
|
assert (! isThreadRunning());
|
|
|
|
stopThread ();
|
|
}
|
|
|
|
//==============================================================================
|
|
// Use a ref-counted object to hold this shared data, so that it can outlive its static
|
|
// shared pointer when threads are still running during static shutdown.
|
|
struct CurrentThreadHolder : public SharedObject
|
|
{
|
|
CurrentThreadHolder() noexcept {}
|
|
|
|
typedef SharedPtr <CurrentThreadHolder> Ptr;
|
|
ThreadLocalValue<Thread*> value;
|
|
};
|
|
|
|
static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros).
|
|
|
|
static SpinLock* castToSpinLockWithoutAliasingWarning (void* s)
|
|
{
|
|
return static_cast<SpinLock*> (s);
|
|
}
|
|
|
|
static CurrentThreadHolder::Ptr getCurrentThreadHolder()
|
|
{
|
|
static CurrentThreadHolder::Ptr currentThreadHolder;
|
|
SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock));
|
|
|
|
if (currentThreadHolder == nullptr)
|
|
currentThreadHolder = new CurrentThreadHolder();
|
|
|
|
return currentThreadHolder;
|
|
}
|
|
|
|
void Thread::threadEntryPoint()
|
|
{
|
|
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
|
currentThreadHolder->value = this;
|
|
|
|
if (threadName.isNotEmpty())
|
|
setCurrentThreadName (threadName);
|
|
|
|
if (startSuspensionEvent.wait (10000))
|
|
{
|
|
bassert (getCurrentThreadId() == threadId);
|
|
|
|
if (affinityMask != 0)
|
|
setCurrentThreadAffinityMask (affinityMask);
|
|
|
|
run();
|
|
}
|
|
|
|
currentThreadHolder->value.releaseCurrentThreadStorage();
|
|
closeThreadHandle();
|
|
}
|
|
|
|
// used to wrap the incoming call from the platform-specific code
|
|
void beast_threadEntryPoint (void* userData)
|
|
{
|
|
static_cast <Thread*> (userData)->threadEntryPoint();
|
|
}
|
|
|
|
//==============================================================================
|
|
void Thread::startThread()
|
|
{
|
|
const RecursiveMutex::ScopedLockType sl (startStopLock);
|
|
|
|
shouldExit = false;
|
|
|
|
if (threadHandle == nullptr)
|
|
{
|
|
launchThread();
|
|
setThreadPriority (threadHandle, threadPriority);
|
|
startSuspensionEvent.signal();
|
|
}
|
|
}
|
|
|
|
void Thread::startThread (const int priority)
|
|
{
|
|
const RecursiveMutex::ScopedLockType sl (startStopLock);
|
|
|
|
if (threadHandle == nullptr)
|
|
{
|
|
threadPriority = priority;
|
|
startThread();
|
|
}
|
|
else
|
|
{
|
|
setPriority (priority);
|
|
}
|
|
}
|
|
|
|
bool Thread::isThreadRunning() const
|
|
{
|
|
return threadHandle != nullptr;
|
|
}
|
|
|
|
Thread* Thread::getCurrentThread()
|
|
{
|
|
return getCurrentThreadHolder()->value.get();
|
|
}
|
|
|
|
//==============================================================================
|
|
void Thread::signalThreadShouldExit()
|
|
{
|
|
shouldExit = true;
|
|
}
|
|
|
|
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
|
|
{
|
|
// Doh! So how exactly do you expect this thread to wait for itself to stop??
|
|
bassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0);
|
|
|
|
const std::uint32_t timeoutEnd = Time::getMillisecondCounter() + (std::uint32_t) timeOutMilliseconds;
|
|
|
|
while (isThreadRunning())
|
|
{
|
|
if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd)
|
|
return false;
|
|
|
|
sleep (2);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Thread::stopThread (const int timeOutMilliseconds)
|
|
{
|
|
bool cleanExit = true;
|
|
|
|
// agh! You can't stop the thread that's calling this method! How on earth
|
|
// would that work??
|
|
bassert (getCurrentThreadId() != getThreadId());
|
|
|
|
const RecursiveMutex::ScopedLockType sl (startStopLock);
|
|
|
|
if (isThreadRunning())
|
|
{
|
|
signalThreadShouldExit();
|
|
notify();
|
|
|
|
if (timeOutMilliseconds != 0)
|
|
{
|
|
cleanExit = waitForThreadToExit (timeOutMilliseconds);
|
|
}
|
|
|
|
if (isThreadRunning())
|
|
{
|
|
bassert (! cleanExit);
|
|
|
|
// very bad karma if this point is reached, as there are bound to be
|
|
// locks and events left in silly states when a thread is killed by force..
|
|
killThread();
|
|
|
|
threadHandle = nullptr;
|
|
threadId = 0;
|
|
|
|
cleanExit = false;
|
|
}
|
|
else
|
|
{
|
|
cleanExit = true;
|
|
}
|
|
}
|
|
|
|
return cleanExit;
|
|
}
|
|
|
|
void Thread::stopThreadAsync ()
|
|
{
|
|
const RecursiveMutex::ScopedLockType sl (startStopLock);
|
|
|
|
if (isThreadRunning())
|
|
{
|
|
signalThreadShouldExit();
|
|
notify();
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
bool Thread::setPriority (const int newPriority)
|
|
{
|
|
// NB: deadlock possible if you try to set the thread prio from the thread itself,
|
|
// so using setCurrentThreadPriority instead in that case.
|
|
if (getCurrentThreadId() == getThreadId())
|
|
return setCurrentThreadPriority (newPriority);
|
|
|
|
const RecursiveMutex::ScopedLockType sl (startStopLock);
|
|
|
|
if (setThreadPriority (threadHandle, newPriority))
|
|
{
|
|
threadPriority = newPriority;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Thread::setCurrentThreadPriority (const int newPriority)
|
|
{
|
|
return setThreadPriority (0, newPriority);
|
|
}
|
|
|
|
void Thread::setAffinityMask (const std::uint32_t newAffinityMask)
|
|
{
|
|
affinityMask = newAffinityMask;
|
|
}
|
|
|
|
//==============================================================================
|
|
bool Thread::wait (const int timeOutMilliseconds) const
|
|
{
|
|
return defaultEvent.wait (timeOutMilliseconds);
|
|
}
|
|
|
|
void Thread::notify() const
|
|
{
|
|
defaultEvent.signal();
|
|
}
|
|
|
|
//==============================================================================
|
|
|
|
// This is here so we dont have circular includes
|
|
//
|
|
void SpinLock::enter() const noexcept
|
|
{
|
|
if (! tryEnter())
|
|
{
|
|
for (int i = 20; --i >= 0;)
|
|
if (tryEnter())
|
|
return;
|
|
|
|
while (! tryEnter())
|
|
Thread::yield();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if BEAST_WINDOWS
|
|
|
|
#include <windows.h>
|
|
#include <process.h>
|
|
#include <tchar.h>
|
|
|
|
namespace beast {
|
|
|
|
HWND beast_messageWindowHandle = 0; // (this is used by other parts of the codebase)
|
|
|
|
void beast_threadEntryPoint (void*);
|
|
|
|
static unsigned int __stdcall threadEntryProc (void* userData)
|
|
{
|
|
if (beast_messageWindowHandle != 0)
|
|
AttachThreadInput (GetWindowThreadProcessId (beast_messageWindowHandle, 0),
|
|
GetCurrentThreadId(), TRUE);
|
|
|
|
beast_threadEntryPoint (userData);
|
|
|
|
_endthreadex (0);
|
|
return 0;
|
|
}
|
|
|
|
void Thread::launchThread()
|
|
{
|
|
unsigned int newThreadId;
|
|
threadHandle = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId);
|
|
threadId = (ThreadID) newThreadId;
|
|
}
|
|
|
|
void Thread::closeThreadHandle()
|
|
{
|
|
CloseHandle ((HANDLE) threadHandle);
|
|
threadId = 0;
|
|
threadHandle = 0;
|
|
}
|
|
|
|
void Thread::killThread()
|
|
{
|
|
if (threadHandle != 0)
|
|
{
|
|
#if BEAST_DEBUG
|
|
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
|
#endif
|
|
TerminateThread (threadHandle, 0);
|
|
}
|
|
}
|
|
|
|
void Thread::setCurrentThreadName (const String& name)
|
|
{
|
|
#if BEAST_DEBUG && BEAST_MSVC
|
|
struct
|
|
{
|
|
DWORD dwType;
|
|
LPCSTR szName;
|
|
DWORD dwThreadID;
|
|
DWORD dwFlags;
|
|
} info;
|
|
|
|
info.dwType = 0x1000;
|
|
info.szName = name.toUTF8();
|
|
info.dwThreadID = GetCurrentThreadId();
|
|
info.dwFlags = 0;
|
|
|
|
__try
|
|
{
|
|
RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info);
|
|
}
|
|
__except (EXCEPTION_CONTINUE_EXECUTION)
|
|
{}
|
|
#else
|
|
(void) name;
|
|
#endif
|
|
}
|
|
|
|
Thread::ThreadID Thread::getCurrentThreadId()
|
|
{
|
|
return (ThreadID) (std::intptr_t) GetCurrentThreadId();
|
|
}
|
|
|
|
bool Thread::setThreadPriority (void* handle, int priority)
|
|
{
|
|
int pri = THREAD_PRIORITY_TIME_CRITICAL;
|
|
|
|
if (priority < 1) pri = THREAD_PRIORITY_IDLE;
|
|
else if (priority < 2) pri = THREAD_PRIORITY_LOWEST;
|
|
else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL;
|
|
else if (priority < 7) pri = THREAD_PRIORITY_NORMAL;
|
|
else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL;
|
|
else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST;
|
|
|
|
if (handle == 0)
|
|
handle = GetCurrentThread();
|
|
|
|
return SetThreadPriority (handle, pri) != FALSE;
|
|
}
|
|
|
|
void Thread::setCurrentThreadAffinityMask (const std::uint32_t affinityMask)
|
|
{
|
|
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
|
}
|
|
|
|
struct SleepEvent
|
|
{
|
|
SleepEvent() noexcept
|
|
: handle (CreateEvent (nullptr, FALSE, FALSE,
|
|
#if BEAST_DEBUG
|
|
_T("BEAST Sleep Event")))
|
|
#else
|
|
nullptr))
|
|
#endif
|
|
{}
|
|
|
|
~SleepEvent() noexcept
|
|
{
|
|
CloseHandle (handle);
|
|
handle = 0;
|
|
}
|
|
|
|
HANDLE handle;
|
|
};
|
|
|
|
static SleepEvent sleepEvent;
|
|
|
|
void Thread::sleep (const int millisecs)
|
|
{
|
|
if (millisecs >= 10 || sleepEvent.handle == 0)
|
|
{
|
|
Sleep ((DWORD) millisecs);
|
|
}
|
|
else
|
|
{
|
|
// unlike Sleep() this is guaranteed to return to the current thread after
|
|
// the time expires, so we'll use this for short waits, which are more likely
|
|
// to need to be accurate
|
|
WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs);
|
|
}
|
|
}
|
|
|
|
void Thread::yield()
|
|
{
|
|
Sleep (0);
|
|
}
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#else
|
|
|
|
#include <time.h>
|
|
#if BEAST_BSD
|
|
// ???
|
|
#elif BEAST_MAC || BEAST_IOS
|
|
#include <Foundation/NSThread.h>
|
|
#include <Foundation/NSString.h>
|
|
#import <objc/message.h>
|
|
namespace beast{
|
|
#include "../../../modules/beast_core/native/osx_ObjCHelpers.h"
|
|
}
|
|
|
|
#else
|
|
# include <sys/prctl.h>
|
|
|
|
#endif
|
|
|
|
namespace beast {
|
|
|
|
void Thread::sleep (int millisecs)
|
|
{
|
|
struct timespec time;
|
|
time.tv_sec = millisecs / 1000;
|
|
time.tv_nsec = (millisecs % 1000) * 1000000;
|
|
nanosleep (&time, nullptr);
|
|
}
|
|
|
|
void beast_threadEntryPoint (void*);
|
|
|
|
extern "C" void* threadEntryProcBeast (void*);
|
|
extern "C" void* threadEntryProcBeast (void* userData)
|
|
{
|
|
BEAST_AUTORELEASEPOOL
|
|
{
|
|
#if BEAST_ANDROID
|
|
struct AndroidThreadScope
|
|
{
|
|
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
|
|
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
|
|
};
|
|
|
|
const AndroidThreadScope androidEnv;
|
|
#endif
|
|
|
|
beast_threadEntryPoint (userData);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Thread::launchThread()
|
|
{
|
|
threadHandle = 0;
|
|
pthread_t handle = 0;
|
|
|
|
if (pthread_create (&handle, 0, threadEntryProcBeast, this) == 0)
|
|
{
|
|
pthread_detach (handle);
|
|
threadHandle = (void*) handle;
|
|
threadId = (ThreadID) threadHandle;
|
|
}
|
|
}
|
|
|
|
void Thread::closeThreadHandle()
|
|
{
|
|
threadId = 0;
|
|
threadHandle = 0;
|
|
}
|
|
|
|
void Thread::killThread()
|
|
{
|
|
if (threadHandle != 0)
|
|
{
|
|
#if BEAST_ANDROID
|
|
bassertfalse; // pthread_cancel not available!
|
|
#else
|
|
pthread_cancel ((pthread_t) threadHandle);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Thread::setCurrentThreadName (const String& name)
|
|
{
|
|
#if BEAST_IOS || (BEAST_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
|
BEAST_AUTORELEASEPOOL
|
|
{
|
|
[[NSThread currentThread] setName: beastStringToNS (name)];
|
|
}
|
|
#elif BEAST_LINUX
|
|
#if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012
|
|
pthread_setname_np (pthread_self(), name.toRawUTF8());
|
|
#else
|
|
prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
bool Thread::setThreadPriority (void* handle, int priority)
|
|
{
|
|
struct sched_param param;
|
|
int policy;
|
|
priority = blimit (0, 10, priority);
|
|
|
|
if (handle == nullptr)
|
|
handle = (void*) pthread_self();
|
|
|
|
if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0)
|
|
return false;
|
|
|
|
policy = priority == 0 ? SCHED_OTHER : SCHED_RR;
|
|
|
|
const int minPriority = sched_get_priority_min (policy);
|
|
const int maxPriority = sched_get_priority_max (policy);
|
|
|
|
param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
|
|
return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0;
|
|
}
|
|
|
|
Thread::ThreadID Thread::getCurrentThreadId()
|
|
{
|
|
return (ThreadID) pthread_self();
|
|
}
|
|
|
|
void Thread::yield()
|
|
{
|
|
sched_yield();
|
|
}
|
|
|
|
//==============================================================================
|
|
/* Remove this macro if you're having problems compiling the cpu affinity
|
|
calls (the API for these has changed about quite a bit in various Linux
|
|
versions, and a lot of distros seem to ship with obsolete versions)
|
|
*/
|
|
#if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES)
|
|
#define SUPPORT_AFFINITIES 1
|
|
#endif
|
|
|
|
void Thread::setCurrentThreadAffinityMask (const std::uint32_t affinityMask)
|
|
{
|
|
#if SUPPORT_AFFINITIES
|
|
cpu_set_t affinity;
|
|
CPU_ZERO (&affinity);
|
|
|
|
for (int i = 0; i < 32; ++i)
|
|
if ((affinityMask & (1 << i)) != 0)
|
|
CPU_SET (i, &affinity);
|
|
|
|
/*
|
|
N.B. If this line causes a compile error, then you've probably not got the latest
|
|
version of glibc installed.
|
|
|
|
If you don't want to update your copy of glibc and don't care about cpu affinities,
|
|
then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0.
|
|
*/
|
|
sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
|
|
sched_yield();
|
|
|
|
#else
|
|
/* affinities aren't supported because either the appropriate header files weren't found,
|
|
or the SUPPORT_AFFINITIES macro was turned off
|
|
*/
|
|
bassertfalse;
|
|
(void) affinityMask;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#endif
|
|
|