mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-02 17:06:00 +00:00
Add Stoppable prepare and start interfaces
This commit is contained in:
@@ -138,8 +138,6 @@ namespace beast
|
||||
#include "containers/DynamicList.cpp"
|
||||
#include "containers/HashMap.cpp"
|
||||
|
||||
#include "diagnostic/Debug.cpp"
|
||||
#include "diagnostic/Error.cpp"
|
||||
#include "diagnostic/FatalError.cpp"
|
||||
#include "diagnostic/FPUFlags.cpp"
|
||||
#include "diagnostic/LeakChecked.cpp"
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "../../beast/Memory.h"
|
||||
#include "../../beast/Intrusive.h"
|
||||
#include "../../beast/Net.h"
|
||||
#include "../../beast/SafeBool.h"
|
||||
#include "../../beast/Strings.h"
|
||||
#include "../../beast/TypeTraits.h"
|
||||
#include "../../beast/Thread.h"
|
||||
@@ -146,7 +147,6 @@ class FileOutputStream;
|
||||
#include "memory/MemoryAlignment.h"
|
||||
#include "memory/CacheLine.h"
|
||||
#include "threads/ReadWriteMutex.h"
|
||||
#include "diagnostic/SafeBool.h"
|
||||
#include "threads/WaitableEvent.h"
|
||||
#include "threads/Thread.h"
|
||||
#include "threads/SpinLock.h"
|
||||
@@ -154,8 +154,6 @@ class FileOutputStream;
|
||||
#include "thread/MutexTraits.h"
|
||||
#include "thread/TrackedMutex.h"
|
||||
#include "diagnostic/FatalError.h"
|
||||
#include "diagnostic/Error.h"
|
||||
#include "diagnostic/Debug.h"
|
||||
#include "text/LexicalCast.h"
|
||||
#include "memory/ContainerDeletePolicy.h"
|
||||
#include "maths/Math.h"
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace Debug
|
||||
{
|
||||
|
||||
void breakPoint ()
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
if (beast_isRunningUnderDebugger ())
|
||||
beast_breakDebugger;
|
||||
|
||||
#else
|
||||
bassertfalse;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_MSVC && defined (_DEBUG)
|
||||
|
||||
#if BEAST_CHECK_MEMORY_LEAKS
|
||||
struct DebugFlagsInitialiser
|
||||
{
|
||||
DebugFlagsInitialiser()
|
||||
{
|
||||
// Activate leak checks on exit in the MSVC Debug CRT (C Runtime)
|
||||
//
|
||||
_CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
}
|
||||
};
|
||||
|
||||
static DebugFlagsInitialiser debugFlagsInitialiser;
|
||||
#endif
|
||||
|
||||
void setAlwaysCheckHeap (bool bAlwaysCheck)
|
||||
{
|
||||
int flags = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
|
||||
|
||||
if (bAlwaysCheck) flags |= _CRTDBG_CHECK_ALWAYS_DF; // on
|
||||
else flags &= ~_CRTDBG_CHECK_ALWAYS_DF; // off
|
||||
|
||||
_CrtSetDbgFlag (flags);
|
||||
}
|
||||
|
||||
void setHeapDelayedFree (bool bDelayedFree)
|
||||
{
|
||||
int flags = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
|
||||
|
||||
if (bDelayedFree) flags |= _CRTDBG_DELAY_FREE_MEM_DF; // on
|
||||
else flags &= ~_CRTDBG_DELAY_FREE_MEM_DF; // off
|
||||
|
||||
_CrtSetDbgFlag (flags);
|
||||
}
|
||||
|
||||
void setHeapReportLeaks (bool bReportLeaks)
|
||||
{
|
||||
int flags = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
|
||||
|
||||
if (bReportLeaks) flags |= _CRTDBG_LEAK_CHECK_DF; // on
|
||||
else flags &= ~_CRTDBG_LEAK_CHECK_DF; // off
|
||||
|
||||
_CrtSetDbgFlag (flags);
|
||||
}
|
||||
|
||||
void reportLeaks ()
|
||||
{
|
||||
_CrtDumpMemoryLeaks ();
|
||||
}
|
||||
|
||||
void checkHeap ()
|
||||
{
|
||||
_CrtCheckMemory ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#else
|
||||
|
||||
void setAlwaysCheckHeap (bool)
|
||||
{
|
||||
}
|
||||
|
||||
void setHeapDelayedFree (bool)
|
||||
{
|
||||
}
|
||||
|
||||
void setHeapReportLeaks (bool)
|
||||
{
|
||||
}
|
||||
|
||||
void reportLeaks ()
|
||||
{
|
||||
}
|
||||
|
||||
void checkHeap ()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
String getSourceLocation (char const* fileName, int lineNumber,
|
||||
int numberOfParents)
|
||||
{
|
||||
return getFileNameFromPath (fileName, numberOfParents) +
|
||||
"(" + String::fromNumber (lineNumber) + ")";
|
||||
}
|
||||
|
||||
String getFileNameFromPath (const char* sourceFileName, int numberOfParents)
|
||||
{
|
||||
String fullPath (sourceFileName);
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
// Convert everything to forward slash
|
||||
fullPath = fullPath.replaceCharacter ('\\', '/');
|
||||
#endif
|
||||
|
||||
String path;
|
||||
|
||||
int chopPoint = fullPath.lastIndexOfChar ('/');
|
||||
path = fullPath.substring (chopPoint + 1);
|
||||
|
||||
while (chopPoint >= 0 && numberOfParents > 0)
|
||||
{
|
||||
--numberOfParents;
|
||||
fullPath = fullPath.substring (0, chopPoint);
|
||||
chopPoint = fullPath.lastIndexOfChar ('/');
|
||||
path = fullPath.substring (chopPoint + 1) + '/' + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Returns a String with double quotes escaped
|
||||
static const String withEscapedQuotes (String const& string)
|
||||
{
|
||||
String escaped;
|
||||
|
||||
int i0 = 0;
|
||||
int i;
|
||||
|
||||
do
|
||||
{
|
||||
i = string.indexOfChar (i0, '"');
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
escaped << string.substring (i0, string.length ());
|
||||
}
|
||||
else
|
||||
{
|
||||
escaped << string.substring (i0, i) << "\\\"";
|
||||
i0 = i + 1;
|
||||
}
|
||||
}
|
||||
while (i != -1);
|
||||
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// Converts escaped quotes back into regular quotes
|
||||
static const String withUnescapedQuotes (String const& string)
|
||||
{
|
||||
String unescaped;
|
||||
|
||||
int i0 = 0;
|
||||
int i;
|
||||
|
||||
do
|
||||
{
|
||||
i = string.indexOfChar (i0, '\\');
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
unescaped << string.substring (i0, string.length ());
|
||||
}
|
||||
else
|
||||
{
|
||||
// peek
|
||||
if (string.length () > i && string[i + 1] == '\"')
|
||||
{
|
||||
unescaped << string.substring (i0, i) << '"';
|
||||
i0 = i + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped << string.substring (i0, i + 1);
|
||||
i0 = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (i != -1);
|
||||
|
||||
return unescaped;
|
||||
}
|
||||
|
||||
// Converts a String that may contain newlines, into a
|
||||
// command line where each line is delimited with quotes.
|
||||
// Any quotes in the actual string will be escaped via \".
|
||||
String stringToCommandLine (String const& string)
|
||||
{
|
||||
String commandLine;
|
||||
|
||||
int i0 = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < string.length (); i++)
|
||||
{
|
||||
beast_wchar c = string[i];
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
if (i0 != 0)
|
||||
commandLine << ' ';
|
||||
|
||||
commandLine << '"' << withEscapedQuotes (string.substring (i0, i)) << '"';
|
||||
i0 = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i0 < i)
|
||||
{
|
||||
if (i0 != 0)
|
||||
commandLine << ' ';
|
||||
|
||||
commandLine << '"' << withEscapedQuotes (string.substring (i0, i)) << '"';
|
||||
}
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
// Converts a command line consisting of multiple quoted strings
|
||||
// back into a single string with newlines delimiting each quoted
|
||||
// string. Escaped quotes \" are turned into real quotes.
|
||||
String commandLineToString (const String& commandLine)
|
||||
{
|
||||
String string;
|
||||
|
||||
bool quoting = false;
|
||||
int i0 = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < commandLine.length (); i++)
|
||||
{
|
||||
beast_wchar c = commandLine[i];
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
// peek
|
||||
if (commandLine.length () > i && commandLine[i + 1] == '\"')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (c == '"')
|
||||
{
|
||||
if (!quoting)
|
||||
{
|
||||
i0 = i + 1;
|
||||
quoting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.isEmpty ())
|
||||
string << '\n';
|
||||
|
||||
string << withUnescapedQuotes (commandLine.substring (i0, i));
|
||||
quoting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A simple unit test to determine the diagnostic settings in a build.
|
||||
//
|
||||
class DebugTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
static int envDebug ()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int beastDebug ()
|
||||
{
|
||||
#ifdef BEAST_DEBUG
|
||||
return BEAST_DEBUG;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int beastForceDebug ()
|
||||
{
|
||||
#ifdef BEAST_FORCE_DEBUG
|
||||
return BEAST_FORCE_DEBUG;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int beastCatchExceptions ()
|
||||
{
|
||||
#ifdef BEAST_CATCH_UNHANDLED_EXCEPTIONS
|
||||
return BEAST_CATCH_UNHANDLED_EXCEPTIONS;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
beginTestCase ("diagnostics");
|
||||
|
||||
logMessage ("operatingSystemName = '" + SystemStats::getOperatingSystemName () + "'");
|
||||
logMessage ("_DEBUG = " + String::fromNumber (envDebug ()));
|
||||
logMessage ("BEAST_DEBUG = " + String::fromNumber (beastDebug ()));
|
||||
logMessage ("BEAST_FORCE_DEBUG = " + String::fromNumber (beastForceDebug ()));
|
||||
logMessage ("BEAST_CATCH_UNHANDLED_EXCEPTIONS = " + String::fromNumber (beastCatchExceptions ()));
|
||||
|
||||
bassertfalse;
|
||||
|
||||
fail ();
|
||||
}
|
||||
|
||||
DebugTests () : UnitTest ("Debug", "beast", runManual)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static DebugTests debugTests;
|
||||
@@ -1,86 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_DEBUG_H_INCLUDED
|
||||
#define BEAST_DEBUG_H_INCLUDED
|
||||
|
||||
// Auxiliary outines for debugging
|
||||
|
||||
namespace Debug
|
||||
{
|
||||
|
||||
/** Break to debugger if a debugger is attached to a debug build.
|
||||
|
||||
Does nothing if no debugger is attached, or the build is not a debug build.
|
||||
*/
|
||||
extern void breakPoint ();
|
||||
|
||||
/** Given a file and line number this formats a suitable string.
|
||||
Usually you will pass __FILE__ and __LINE__ here.
|
||||
*/
|
||||
String getSourceLocation (char const* fileName, int lineNumber,
|
||||
int numberOfParents = 0);
|
||||
|
||||
/** Retrieve the file name from a full path.
|
||||
The nubmer of parents can be chosen
|
||||
*/
|
||||
String getFileNameFromPath (const char* sourceFileName, int numberOfParents = 0);
|
||||
|
||||
// Convert a String that may contain double quotes and newlines
|
||||
// into a String with double quotes escaped as \" and each
|
||||
// line as a separate quoted command line argument.
|
||||
String stringToCommandLine (const String& s);
|
||||
|
||||
// Convert a quoted and escaped command line back into a String
|
||||
// that can contain newlines and double quotes.
|
||||
String commandLineToString (const String& commandLine);
|
||||
|
||||
//
|
||||
// These control the MSVC C Runtime Debug heap.
|
||||
//
|
||||
// The calls currently do nothing on other platforms.
|
||||
//
|
||||
|
||||
/** Calls checkHeap() at every allocation and deallocation.
|
||||
*/
|
||||
extern void setAlwaysCheckHeap (bool bAlwaysCheck);
|
||||
|
||||
/** Keep freed memory blocks in the heap's linked list, assign them the
|
||||
_FREE_BLOCK type, and fill them with the byte value 0xDD.
|
||||
*/
|
||||
extern void setHeapDelayedFree (bool bDelayedFree);
|
||||
|
||||
/** Perform automatic leak checking at program exit through a call to
|
||||
dumpMemoryLeaks() and generate an error report if the application
|
||||
failed to free all the memory it allocated.
|
||||
*/
|
||||
extern void setHeapReportLeaks (bool bReportLeaks);
|
||||
|
||||
/** Report all memory blocks which have not been freed.
|
||||
*/
|
||||
extern void reportLeaks ();
|
||||
|
||||
/** Confirms the integrity of the memory blocks allocated in the
|
||||
debug heap (debug version only.
|
||||
*/
|
||||
extern void checkHeap ();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,243 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Error::Error ()
|
||||
: m_code (success)
|
||||
, m_lineNumber (0)
|
||||
, m_needsToBeChecked (true)
|
||||
, m_szWhat (0)
|
||||
{
|
||||
}
|
||||
|
||||
Error::Error (Error const& other)
|
||||
: m_code (other.m_code)
|
||||
, m_reasonText (other.m_reasonText)
|
||||
, m_sourceFileName (other.m_sourceFileName)
|
||||
, m_lineNumber (other.m_lineNumber)
|
||||
, m_needsToBeChecked (true)
|
||||
, m_szWhat (0)
|
||||
{
|
||||
other.m_needsToBeChecked = false;
|
||||
}
|
||||
|
||||
Error::~Error () noexcept
|
||||
{
|
||||
/* If this goes off it means an error object was created but never tested */
|
||||
bassert (!m_needsToBeChecked);
|
||||
}
|
||||
|
||||
Error& Error::operator= (Error const& other)
|
||||
{
|
||||
m_code = other.m_code;
|
||||
m_reasonText = other.m_reasonText;
|
||||
m_sourceFileName = other.m_sourceFileName;
|
||||
m_lineNumber = other.m_lineNumber;
|
||||
m_needsToBeChecked = true;
|
||||
m_what = String::empty;
|
||||
m_szWhat = 0;
|
||||
|
||||
other.m_needsToBeChecked = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Error::Code Error::code () const
|
||||
{
|
||||
m_needsToBeChecked = false;
|
||||
return m_code;
|
||||
}
|
||||
|
||||
bool Error::failed () const
|
||||
{
|
||||
return code () != success;
|
||||
}
|
||||
|
||||
bool Error::asBoolean () const
|
||||
{
|
||||
return code () != success;
|
||||
}
|
||||
|
||||
String const Error::getReasonText () const
|
||||
{
|
||||
return m_reasonText;
|
||||
}
|
||||
|
||||
String const Error::getSourceFilename () const
|
||||
{
|
||||
return m_sourceFileName;
|
||||
}
|
||||
|
||||
int Error::getLineNumber () const
|
||||
{
|
||||
return m_lineNumber;
|
||||
}
|
||||
|
||||
Error& Error::fail (char const* sourceFileName,
|
||||
int lineNumber,
|
||||
String const reasonText,
|
||||
Code errorCode)
|
||||
{
|
||||
bassert (m_code == success);
|
||||
bassert (errorCode != success);
|
||||
|
||||
m_code = errorCode;
|
||||
m_reasonText = reasonText;
|
||||
m_sourceFileName = Debug::getFileNameFromPath (sourceFileName);
|
||||
m_lineNumber = lineNumber;
|
||||
m_needsToBeChecked = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Error& Error::fail (char const* sourceFileName,
|
||||
int lineNumber,
|
||||
Code errorCode)
|
||||
{
|
||||
return fail (sourceFileName,
|
||||
lineNumber,
|
||||
getReasonTextForCode (errorCode),
|
||||
errorCode);
|
||||
}
|
||||
|
||||
void Error::reset ()
|
||||
{
|
||||
m_code = success;
|
||||
m_reasonText = String::empty;
|
||||
m_sourceFileName = String::empty;
|
||||
m_lineNumber = 0;
|
||||
m_needsToBeChecked = true;
|
||||
m_what = String::empty;
|
||||
m_szWhat = 0;
|
||||
}
|
||||
|
||||
void Error::willBeReported () const
|
||||
{
|
||||
m_needsToBeChecked = false;
|
||||
}
|
||||
|
||||
char const* Error::what () const noexcept
|
||||
{
|
||||
if (! m_szWhat)
|
||||
{
|
||||
// The application could not be initialized because sqlite was denied access permission
|
||||
// The application unexpectedly quit because the exception 'sqlite was denied access permission at file ' was thrown
|
||||
m_what <<
|
||||
m_reasonText << " " <<
|
||||
TRANS ("at file") << " '" <<
|
||||
m_sourceFileName << "' " <<
|
||||
TRANS ("line") << " " <<
|
||||
String (m_lineNumber) << " " <<
|
||||
TRANS ("with code") << " = " <<
|
||||
String (m_code);
|
||||
|
||||
m_szWhat = (const char*)m_what.toUTF8 ();
|
||||
}
|
||||
|
||||
return m_szWhat;
|
||||
}
|
||||
|
||||
String const Error::getReasonTextForCode (Code code)
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case success:
|
||||
s = TRANS ("the operation was successful");
|
||||
break;
|
||||
|
||||
case general:
|
||||
s = TRANS ("a general error occurred");
|
||||
break;
|
||||
|
||||
case canceled:
|
||||
s = TRANS ("the operation was canceled");
|
||||
break;
|
||||
|
||||
case exception:
|
||||
s = TRANS ("an exception was thrown");
|
||||
break;
|
||||
|
||||
case unexpected:
|
||||
s = TRANS ("an unexpected result was encountered");
|
||||
break;
|
||||
|
||||
case platform:
|
||||
s = TRANS ("a system exception was signaled");
|
||||
break;
|
||||
|
||||
case noMemory:
|
||||
s = TRANS ("there was not enough memory");
|
||||
break;
|
||||
|
||||
case noMoreData:
|
||||
s = TRANS ("the end of data was reached");
|
||||
break;
|
||||
|
||||
case invalidData:
|
||||
s = TRANS ("the data is corrupt or invalid");
|
||||
break;
|
||||
|
||||
case bufferSpace:
|
||||
s = TRANS ("the buffer is too small");
|
||||
break;
|
||||
|
||||
case badParameter:
|
||||
s = TRANS ("one or more parameters were invalid");
|
||||
break;
|
||||
|
||||
case assertFailed:
|
||||
s = TRANS ("an assertion failed");
|
||||
break;
|
||||
|
||||
case fileInUse:
|
||||
s = TRANS ("the file is in use");
|
||||
break;
|
||||
|
||||
case fileExists:
|
||||
s = TRANS ("the file exists");
|
||||
break;
|
||||
|
||||
case fileNoPerm:
|
||||
s = TRANS ("permission was denied");
|
||||
break;
|
||||
|
||||
case fileIOError:
|
||||
s = TRANS ("an I/O or device error occurred");
|
||||
break;
|
||||
|
||||
case fileNoSpace:
|
||||
s = TRANS ("there is no space left on the device");
|
||||
break;
|
||||
|
||||
case fileNotFound:
|
||||
s = TRANS ("the file was not found");
|
||||
break;
|
||||
|
||||
case fileNameInvalid:
|
||||
s = TRANS ("the file name was illegal or malformed");
|
||||
break;
|
||||
|
||||
default:
|
||||
s = TRANS ("an unknown error code was received");
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ERROR_H_INCLUDED
|
||||
#define BEAST_ERROR_H_INCLUDED
|
||||
|
||||
/** A concise error report.
|
||||
|
||||
This lightweight but flexible class records lets you record the file and
|
||||
line where a recoverable error occurred, along with some optional human
|
||||
readable text.
|
||||
|
||||
A recoverable error can be passed along and turned into a non recoverable
|
||||
error by throwing the object: it's derivation from std::exception is
|
||||
fully compliant with the C++ exception interface.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API Error
|
||||
: public std::exception
|
||||
, public SafeBool <Error>
|
||||
{
|
||||
public:
|
||||
/** Numeric code.
|
||||
|
||||
This enumeration is useful when the caller needs to take different
|
||||
actions depending on the failure. For example, trying again later if
|
||||
a file is locked.
|
||||
*/
|
||||
enum Code
|
||||
{
|
||||
success, //!< "the operation was successful"
|
||||
|
||||
general, //!< "a general error occurred"
|
||||
|
||||
canceled, //!< "the operation was canceled"
|
||||
exception, //!< "an exception was thrown"
|
||||
unexpected, //!< "an unexpected result was encountered"
|
||||
platform, //!< "a system exception was signaled"
|
||||
|
||||
noMemory, //!< "there was not enough memory"
|
||||
noMoreData, //!< "the end of data was reached"
|
||||
invalidData, //!< "the data is corrupt or invalid"
|
||||
bufferSpace, //!< "the buffer is too small"
|
||||
badParameter, //!< "one or more parameters were invalid"
|
||||
assertFailed, //!< "an assertion failed"
|
||||
|
||||
fileInUse, //!< "the file is in use"
|
||||
fileExists, //!< "the file exists"
|
||||
fileNoPerm, //!< "permission was denied" (file attributes conflict)
|
||||
fileIOError, //!< "an I/O or device error occurred"
|
||||
fileNoSpace, //!< "there is no space left on the device"
|
||||
fileNotFound, //!< "the file was not found"
|
||||
fileNameInvalid //!< "the file name was illegal or malformed"
|
||||
};
|
||||
|
||||
Error ();
|
||||
Error (Error const& other);
|
||||
Error& operator= (Error const& other);
|
||||
|
||||
virtual ~Error () noexcept;
|
||||
|
||||
Code code () const;
|
||||
bool failed () const;
|
||||
|
||||
bool asBoolean () const;
|
||||
|
||||
String const getReasonText () const;
|
||||
String const getSourceFilename () const;
|
||||
int getLineNumber () const;
|
||||
|
||||
Error& fail (char const* sourceFileName,
|
||||
int lineNumber,
|
||||
String const reasonText,
|
||||
Code errorCode = general);
|
||||
|
||||
Error& fail (char const* sourceFileName,
|
||||
int lineNumber,
|
||||
Code errorCode = general);
|
||||
|
||||
// A function that is capable of recovering from an error (for
|
||||
// example, by performing a different action) can reset the
|
||||
// object so it can be passed up.
|
||||
void reset ();
|
||||
|
||||
// Call this when reporting the error to clear the "checked" flag
|
||||
void willBeReported () const;
|
||||
|
||||
// for std::exception. This lets you throw an Error that should
|
||||
// terminate the application. The what() message will be less
|
||||
// descriptive so ideally you should catch the Error object instead.
|
||||
char const* what () const noexcept;
|
||||
|
||||
static String const getReasonTextForCode (Code code);
|
||||
|
||||
private:
|
||||
Code m_code;
|
||||
String m_reasonText;
|
||||
String m_sourceFileName;
|
||||
int m_lineNumber;
|
||||
mutable bool m_needsToBeChecked;
|
||||
mutable String m_what; // created on demand
|
||||
mutable char const* m_szWhat;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SAFEBOOL_H_INCLUDED
|
||||
#define BEAST_SAFEBOOL_H_INCLUDED
|
||||
|
||||
/** Safe evaluation of class as `bool`.
|
||||
|
||||
This allows a class to be safely evaluated as a bool without the usual
|
||||
harmful side effects of the straightforward operator conversion approach.
|
||||
To use it, derive your class from SafeBool and implement `asBoolean()` as:
|
||||
|
||||
@code
|
||||
|
||||
bool asBoolean () const;
|
||||
|
||||
@endcode
|
||||
|
||||
Ideas from http://www.artima.com/cppsource/safebool.html
|
||||
|
||||
@class SafeBool
|
||||
*/
|
||||
class BEAST_API SafeBoolBase
|
||||
{
|
||||
private:
|
||||
void disallowed () const { }
|
||||
|
||||
public:
|
||||
void allowed () const { }
|
||||
|
||||
protected:
|
||||
typedef void (SafeBoolBase::*boolean_t) () const;
|
||||
|
||||
SafeBoolBase () { }
|
||||
SafeBoolBase (SafeBoolBase const&) { }
|
||||
SafeBoolBase& operator= (SafeBoolBase const&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
~SafeBoolBase () { }
|
||||
};
|
||||
|
||||
template <typename T = void>
|
||||
class SafeBool : public SafeBoolBase
|
||||
{
|
||||
public:
|
||||
operator boolean_t () const
|
||||
{
|
||||
return (static_cast <T const*> (this))->asBoolean ()
|
||||
? &SafeBoolBase::allowed : 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
~SafeBool () { }
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
void operator== (SafeBool <T> const& lhs, SafeBool <U> const& rhs)
|
||||
{
|
||||
lhs.disallowed ();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void operator!= (SafeBool <T> const& lhs, SafeBool <U> const& rhs)
|
||||
{
|
||||
lhs.disallowed ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -17,84 +17,45 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
Stoppable::Stoppable (char const* name, Stoppable& parent)
|
||||
Stoppable::Stoppable (char const* name, RootStoppable& root)
|
||||
: m_name (name)
|
||||
, m_root (false)
|
||||
, m_root (root)
|
||||
, m_child (this)
|
||||
, m_calledStop (false)
|
||||
, m_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
// must not have had stop called
|
||||
}
|
||||
|
||||
Stoppable::Stoppable (char const* name, Stoppable& parent)
|
||||
: m_name (name)
|
||||
, m_root (parent.m_root)
|
||||
, m_child (this)
|
||||
, m_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
// Must not have stopping parent.
|
||||
bassert (! parent.isStopping());
|
||||
|
||||
parent.m_children.push_front (&m_child);
|
||||
}
|
||||
|
||||
Stoppable::Stoppable (char const* name, Stoppable* parent)
|
||||
: m_name (name)
|
||||
, m_root (parent == nullptr)
|
||||
, m_child (this)
|
||||
, m_calledStop (false)
|
||||
, m_stopped (false)
|
||||
, m_childrenStopped (false)
|
||||
{
|
||||
if (parent != nullptr)
|
||||
{
|
||||
// must not have had stop called
|
||||
bassert (! parent->isStopping());
|
||||
|
||||
parent->m_children.push_front (&m_child);
|
||||
}
|
||||
}
|
||||
|
||||
Stoppable::~Stoppable ()
|
||||
{
|
||||
// must be stopped
|
||||
bassert (m_stopped);
|
||||
|
||||
// children must be stopped
|
||||
// Children must be stopped.
|
||||
bassert (m_childrenStopped);
|
||||
}
|
||||
|
||||
void Stoppable::stop (Journal::Stream stream)
|
||||
bool Stoppable::isStopping() const
|
||||
{
|
||||
// may only be called once
|
||||
if (m_calledStop)
|
||||
return;
|
||||
|
||||
m_calledStop = true;
|
||||
|
||||
// must be called from a root stoppable
|
||||
bassert (m_root);
|
||||
|
||||
// send the notification
|
||||
stopAsync ();
|
||||
|
||||
// now block on the tree of Stoppable objects from the leaves up.
|
||||
stopRecursive (stream);
|
||||
return m_root.isStopping();
|
||||
}
|
||||
|
||||
void Stoppable::stopAsync ()
|
||||
{
|
||||
// must be called from a root stoppable
|
||||
bassert (m_root);
|
||||
|
||||
stopAsyncRecursive ();
|
||||
}
|
||||
|
||||
bool Stoppable::isStopping ()
|
||||
{
|
||||
return m_calledStopAsync.get() != 0;
|
||||
}
|
||||
|
||||
bool Stoppable::isStopped ()
|
||||
bool Stoppable::isStopped () const
|
||||
{
|
||||
return m_stopped;
|
||||
}
|
||||
|
||||
bool Stoppable::areChildrenStopped ()
|
||||
bool Stoppable::areChildrenStopped () const
|
||||
{
|
||||
return m_childrenStopped;
|
||||
}
|
||||
@@ -104,75 +65,134 @@ void Stoppable::stopped ()
|
||||
m_stoppedEvent.signal();
|
||||
}
|
||||
|
||||
void Stoppable::onStop()
|
||||
void Stoppable::onPrepare (Journal journal)
|
||||
{
|
||||
}
|
||||
|
||||
void Stoppable::onStart (Journal journal)
|
||||
{
|
||||
}
|
||||
|
||||
void Stoppable::onStop (Journal journal)
|
||||
{
|
||||
stopped();
|
||||
}
|
||||
|
||||
void Stoppable::onChildrenStopped ()
|
||||
void Stoppable::onChildrenStopped (Journal journal)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Stoppable::stopAsyncRecursive ()
|
||||
void Stoppable::prepareRecursive (Journal journal)
|
||||
{
|
||||
// make sure we only do this once
|
||||
if (m_root)
|
||||
{
|
||||
// if this fails, some other thread got to it first
|
||||
if (! m_calledStopAsync.compareAndSetBool (1, 0))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't possibly already be set
|
||||
bassert (m_calledStopAsync.get() == 0);
|
||||
|
||||
m_calledStopAsync.set (1);
|
||||
}
|
||||
|
||||
// notify this stoppable
|
||||
onStop ();
|
||||
|
||||
// notify children
|
||||
onPrepare (journal);
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
{
|
||||
iter->stoppable->stopAsyncRecursive();
|
||||
}
|
||||
iter->stoppable->prepareRecursive (journal);
|
||||
}
|
||||
|
||||
void Stoppable::stopRecursive (Journal::Stream stream)
|
||||
void Stoppable::startRecursive (Journal journal)
|
||||
{
|
||||
// Block on each child recursively. Thinking of the Stoppable
|
||||
// hierarchy as a tree with the root at the top, we will block
|
||||
// first on leaves, and then at each successivly higher level.
|
||||
onStart (journal);
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->startRecursive (journal);
|
||||
}
|
||||
|
||||
void Stoppable::stopAsyncRecursive (Journal journal)
|
||||
{
|
||||
onStop (journal);
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
iter->stoppable->stopAsyncRecursive (journal);
|
||||
}
|
||||
|
||||
void Stoppable::stopRecursive (Journal journal)
|
||||
{
|
||||
// Block on each child from the bottom of the tree up.
|
||||
//
|
||||
for (Children::const_iterator iter (m_children.cbegin ());
|
||||
iter != m_children.cend(); ++iter)
|
||||
{
|
||||
iter->stoppable->stopRecursive (stream);
|
||||
}
|
||||
iter->stoppable->stopRecursive (journal);
|
||||
|
||||
// Once we get here, we either have no children, or all of
|
||||
// our children have stopped, so update state accordingly.
|
||||
// if we get here then all children have stopped
|
||||
//
|
||||
memoryBarrier ();
|
||||
m_childrenStopped = true;
|
||||
onChildrenStopped (journal);
|
||||
|
||||
// Notify derived class that children have stopped.
|
||||
onChildrenStopped ();
|
||||
|
||||
// Block until this stoppable stops. First we do a timed wait of 1 second, and
|
||||
// if that times out we report to the Journal and then do an infinite wait.
|
||||
// Now block on this Stoppable.
|
||||
//
|
||||
bool const timedOut (! m_stoppedEvent.wait (1 * 1000)); // milliseconds
|
||||
if (timedOut)
|
||||
{
|
||||
stream << "Waiting for '" << m_name << "' to stop";
|
||||
journal.warning << "Waiting for '" << m_name << "' to stop";
|
||||
m_stoppedEvent.wait ();
|
||||
}
|
||||
|
||||
// once we get here, we know the stoppable has stopped.
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
RootStoppable::RootStoppable (char const* name)
|
||||
: Stoppable (name, *this)
|
||||
{
|
||||
}
|
||||
|
||||
RootStoppable::~RootStoppable ()
|
||||
{
|
||||
}
|
||||
|
||||
bool RootStoppable::isStopping() const
|
||||
{
|
||||
return m_calledStopAsync.get() != 0;
|
||||
}
|
||||
|
||||
void RootStoppable::prepare (Journal journal)
|
||||
{
|
||||
if (! m_prepared.compareAndSetBool (1, 0))
|
||||
{
|
||||
journal.warning << "Stoppable::prepare called again";
|
||||
return;
|
||||
}
|
||||
|
||||
prepareRecursive (journal);
|
||||
}
|
||||
|
||||
void RootStoppable::start (Journal journal)
|
||||
{
|
||||
// Courtesy call to prepare.
|
||||
if (m_prepared.compareAndSetBool (1, 0))
|
||||
prepareRecursive (journal);
|
||||
|
||||
if (! m_started.compareAndSetBool (1, 0))
|
||||
{
|
||||
journal.warning << "Stoppable::start called again";
|
||||
return;
|
||||
}
|
||||
|
||||
startRecursive (journal);
|
||||
}
|
||||
|
||||
void RootStoppable::stop (Journal journal)
|
||||
{
|
||||
if (! m_calledStop.compareAndSetBool (1, 0))
|
||||
{
|
||||
journal.warning << "Stoppable::stop called again";
|
||||
return;
|
||||
}
|
||||
|
||||
stopAsync (journal);
|
||||
stopRecursive (journal);
|
||||
}
|
||||
|
||||
void RootStoppable::stopAsync (Journal journal)
|
||||
{
|
||||
if (! m_calledStopAsync.compareAndSetBool (1, 0))
|
||||
return;
|
||||
|
||||
stopAsyncRecursive (journal);
|
||||
}
|
||||
|
||||
@@ -20,16 +20,66 @@
|
||||
#ifndef BEAST_CORE_STOPPABLE_H_INCLUDED
|
||||
#define BEAST_CORE_STOPPABLE_H_INCLUDED
|
||||
|
||||
/** Provides an interface for stopping.
|
||||
class RootStoppable;
|
||||
|
||||
/** Provides an interface for starting and stopping.
|
||||
|
||||
A common method of structuring server or peer to peer code is to isolate
|
||||
conceptual portions of functionality into individual classes, aggregated
|
||||
into some larger "application" or "core" object which holds all the parts.
|
||||
Frequently, these components are dependent on each other in unavoidably
|
||||
complex ways. They also often use threads and perform asynchronous i/o
|
||||
operations involving sockets or other operating system objects. The process
|
||||
of starting and stopping such a system can be complex. This interface
|
||||
provides a set of behaviors for ensuring that the start and stop of a
|
||||
composite application-style object is well defined.
|
||||
|
||||
Upon the initialization of the composite object these steps are peformed:
|
||||
|
||||
1. Construct sub-components.
|
||||
|
||||
These are all typically derived from Stoppable. There can be a deep
|
||||
hierarchy: Stoppable objects may themselves have Stoppable child
|
||||
objects. This captures the relationship of dependencies.
|
||||
|
||||
2. prepare()
|
||||
|
||||
Because some components may depend on others, preparatory steps require
|
||||
that all objects be first constructed. The prepare step calls all
|
||||
Stoppable objects in the tree starting from the leaves and working up
|
||||
to the root. In this stage we are guaranteed that all objects have been
|
||||
constructed and are in a well-defined state.
|
||||
|
||||
3. onPrepare()
|
||||
|
||||
This override is called for all Stoppable objects in the hierarchy
|
||||
during the prepare stage. Objects are called from the bottom up.
|
||||
It is guaranteed that all child Stoppable objects have already been
|
||||
prepared when this is called.
|
||||
|
||||
4. start()
|
||||
|
||||
At this point all sub-components have been constructed and prepared,
|
||||
so it should be safe for them to be started. While some Stoppable
|
||||
objects may do nothing in their start function, others will start
|
||||
threads or call asynchronous i/o initiating functions like timers or
|
||||
sockets.
|
||||
|
||||
5. onStart()
|
||||
|
||||
This override is called for all Stoppable objects in the hierarchy
|
||||
during the start stage. Objects are called from the bottom up.
|
||||
It is guaranteed that all child Stoppable objects have already been
|
||||
started when this is called.
|
||||
|
||||
This is the sequence of events involved in stopping:
|
||||
|
||||
1. stopAsync() [optional]
|
||||
6. stopAsync() [optional]
|
||||
|
||||
This notifies the root Stoppable and all its children that a stop is
|
||||
requested.
|
||||
|
||||
2. stop()
|
||||
7. stop()
|
||||
|
||||
This first calls stopAsync(), and then blocks on each child Stoppable in
|
||||
the in the tree from the bottom up, until the Stoppable indicates it has
|
||||
@@ -37,21 +87,21 @@
|
||||
when some external signal indicates that the process should stop. For
|
||||
example, an RPC 'stop' command, or a SIGINT POSIX signal.
|
||||
|
||||
3. onStop()
|
||||
8. onStop()
|
||||
|
||||
This override is called for the root Stoppable and all its children when
|
||||
stopAsync() is called. Derived classes should cancel pending I/O and
|
||||
timers, signal that threads should exit, queue cleanup jobs, and perform
|
||||
any other necessary final actions in preparation for exit.
|
||||
|
||||
4. onChildrenStopped()
|
||||
9. onChildrenStopped()
|
||||
|
||||
This override is called when all the children have stopped. This informs
|
||||
the Stoppable that there should not be any more dependents making calls
|
||||
into its member functions. A Stoppable that has no children will still
|
||||
have this function called.
|
||||
|
||||
5. stopped()
|
||||
10. stopped()
|
||||
|
||||
The derived class calls this function to inform the Stoppable API that
|
||||
it has completed the stop. This unblocks the caller of stop().
|
||||
@@ -102,85 +152,44 @@
|
||||
|
||||
@note A Stoppable may not be restarted.
|
||||
*/
|
||||
/** @{ */
|
||||
class Stoppable
|
||||
{
|
||||
public:
|
||||
/** Create the stoppable.
|
||||
A stoppable without a parent is a root stoppable.
|
||||
@param name A name used in log output.
|
||||
@param parent Optional parent of this stoppable.
|
||||
*/
|
||||
/** @{ */
|
||||
Stoppable (char const* name, Stoppable& parent);
|
||||
explicit Stoppable (char const* name, Stoppable* parent = nullptr);
|
||||
/** @} */
|
||||
protected:
|
||||
Stoppable (char const* name, RootStoppable& root);
|
||||
|
||||
/** Destroy the stoppable.
|
||||
Undefined behavior results if the object is not stopped first.
|
||||
Stoppable objects should not be created and destroyed dynamically during
|
||||
the process lifetime. Rather, the set of stoppables should be static and
|
||||
well-defined after initialization. If the set of domain-specific objects
|
||||
which need to stop is dynamic, use a single parent Stoppable to manage
|
||||
those objects. For example, make an HTTP server implementation a
|
||||
Stoppable, rather than each of its active connections.
|
||||
*/
|
||||
public:
|
||||
/** Create the Stoppable. */
|
||||
Stoppable (char const* name, Stoppable& parent);
|
||||
|
||||
/** Destroy the Stoppable. */
|
||||
virtual ~Stoppable ();
|
||||
|
||||
/** Notify a root stoppable and children to stop, and block until stopped.
|
||||
Has no effect if the stoppable was already notified.
|
||||
This blocks until the stoppable and all of its children have stopped.
|
||||
@param stream An optional Journal::Stream on which to log progress.
|
||||
/** Returns `true` if the stoppable should stop. */
|
||||
bool isStopping () const;
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread not associated with a Stoppable.
|
||||
*/
|
||||
void stop (Journal::Stream stream = Journal::Stream());
|
||||
/** Returns `true` if the requested stop has completed. */
|
||||
bool isStopped () const;
|
||||
|
||||
/** Notify a root stoppable and children to stop, without waiting.
|
||||
Has no effect if the stoppable was already notified.
|
||||
/** Returns `true` if all children have stopped. */
|
||||
bool areChildrenStopped () const;
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
void stopAsync ();
|
||||
|
||||
/** Returns `true` if the stoppable should stop.
|
||||
Call from the derived class to determine if a long-running operation
|
||||
should be canceled. This is not appropriate for either threads, or
|
||||
asynchronous I/O. For threads, use the thread-specific facilities
|
||||
available to inform the thread that it should exit. For asynchronous
|
||||
I/O, cancel all pending operations inside the onStop override.
|
||||
@see onStop
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
bool isStopping ();
|
||||
|
||||
/** Returns `true` if the stoppable has completed its stop.
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
bool isStopped ();
|
||||
|
||||
/** Returns `true` if all children have stopped.
|
||||
For stoppables without children, this returns `true` immediately
|
||||
after a stop notification is received.
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
bool areChildrenStopped ();
|
||||
|
||||
/** Called by derived classes to indicate that the stoppable has stopped.
|
||||
The derived class must call this either after isStopping returns `true`,
|
||||
or when onStop is called, or else the call to stop will never unblock.
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
/** Called by derived classes to indicate that the stoppable has stopped. */
|
||||
void stopped ();
|
||||
|
||||
/** Override called during preparation.
|
||||
Since all other Stoppable objects in the tree have already been
|
||||
constructed, this provides an opportunity to perform initialization which
|
||||
depends on calling into other Stoppable objects.
|
||||
This call is made on the same thread that called prepare().
|
||||
The default implementation does nothing.
|
||||
Guaranteed to only be called once.
|
||||
*/
|
||||
virtual void onPrepare (Journal journal);
|
||||
|
||||
/** Override called during start. */
|
||||
virtual void onStart (Journal journal);
|
||||
|
||||
/** Override called when the stop notification is issued.
|
||||
|
||||
The call is made on an unspecified, implementation-specific thread.
|
||||
@@ -202,7 +211,7 @@ public:
|
||||
Guaranteed only to be called once.
|
||||
Must be safe to call from any thread at any time.
|
||||
*/
|
||||
virtual void onStop ();
|
||||
virtual void onStop (Journal journal);
|
||||
|
||||
/** Override called when all children have stopped.
|
||||
|
||||
@@ -222,9 +231,11 @@ public:
|
||||
Guaranteed only to be called once.
|
||||
Must be safe to call from any thread at any time.
|
||||
*/
|
||||
virtual void onChildrenStopped ();
|
||||
virtual void onChildrenStopped (Journal journal);
|
||||
|
||||
private:
|
||||
friend class RootStoppable;
|
||||
|
||||
struct Child;
|
||||
typedef LockFreeStack <Child> Children;
|
||||
|
||||
@@ -237,28 +248,70 @@ private:
|
||||
Stoppable* stoppable;
|
||||
};
|
||||
|
||||
void stopAsyncRecursive ();
|
||||
void stopRecursive (Journal::Stream stream);
|
||||
void prepareRecursive (Journal journal);
|
||||
void startRecursive (Journal journal);
|
||||
void stopAsyncRecursive (Journal journal);
|
||||
void stopRecursive (Journal journal);
|
||||
|
||||
protected:
|
||||
char const* m_name;
|
||||
bool m_root;
|
||||
RootStoppable& m_root;
|
||||
Child m_child;
|
||||
Children m_children;
|
||||
|
||||
// Flag that we called stop. This is for diagnostics.
|
||||
bool m_calledStop;
|
||||
|
||||
// Atomic flag to make sure we only call stopAsync once.
|
||||
Atomic <int> m_calledStopAsync;
|
||||
|
||||
// Flag that this service stopped. Never goes back to false.
|
||||
bool volatile m_stopped;
|
||||
|
||||
// Flag that all children have stopped (recursive). Never goes back to false.
|
||||
bool volatile m_childrenStopped;
|
||||
|
||||
// stop() blocks on this event until stopped() is called.
|
||||
Children m_children;
|
||||
WaitableEvent m_stoppedEvent;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class RootStoppable : public Stoppable
|
||||
{
|
||||
public:
|
||||
explicit RootStoppable (char const* name);
|
||||
|
||||
~RootStoppable ();
|
||||
|
||||
bool isStopping() const;
|
||||
|
||||
/** Prepare all contained Stoppable objects.
|
||||
This calls onPrepare for all Stoppable objects in the tree.
|
||||
Calls made after the first have no effect.
|
||||
Thread safety:
|
||||
May be called from any thread.
|
||||
*/
|
||||
void prepare (Journal journal = Journal());
|
||||
|
||||
/** Start all contained Stoppable objects.
|
||||
The default implementation does nothing.
|
||||
Calls made after the first have no effect.
|
||||
Thread safety:
|
||||
May be called from any thread.
|
||||
*/
|
||||
void start (Journal journal = Journal());
|
||||
|
||||
/** Notify a root stoppable and children to stop, and block until stopped.
|
||||
Has no effect if the stoppable was already notified.
|
||||
This blocks until the stoppable and all of its children have stopped.
|
||||
Thread safety:
|
||||
Safe to call from any thread not associated with a Stoppable.
|
||||
*/
|
||||
void stop (Journal journal = Journal());
|
||||
|
||||
/** Notify a root stoppable and children to stop, without waiting.
|
||||
Has no effect if the stoppable was already notified.
|
||||
|
||||
Thread safety:
|
||||
Safe to call from any thread at any time.
|
||||
*/
|
||||
void stopAsync (Journal journal = Journal());
|
||||
|
||||
private:
|
||||
Atomic <int> m_prepared;
|
||||
Atomic <int> m_started;
|
||||
Atomic <int> m_calledStop;
|
||||
Atomic <int> m_calledStopAsync;
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user