Beast class refactor

This commit is contained in:
Vinnie Falco
2013-10-03 17:38:04 -07:00
parent 8b7056b06c
commit ac37c38133
35 changed files with 59 additions and 5641 deletions

View File

@@ -133,6 +133,9 @@
<ClInclude Include="..\..\beast\Net.h" />
<ClInclude Include="..\..\beast\net\IPEndpoint.h" />
<ClInclude Include="..\..\beast\SafeBool.h" />
<ClInclude Include="..\..\beast\SmartPtr.h" />
<ClInclude Include="..\..\beast\smart_ptr\ContainerDeletePolicy.h" />
<ClInclude Include="..\..\beast\smart_ptr\ScopedPointer.h" />
<ClInclude Include="..\..\beast\StaticAssert.h" />
<ClInclude Include="..\..\beast\Strings.h" />
<ClInclude Include="..\..\beast\strings\CharacterFunctions.h" />
@@ -228,7 +231,6 @@
<ClInclude Include="..\..\modules\beast_core\containers\ScopedValueSetter.h" />
<ClInclude Include="..\..\modules\beast_core\containers\SortedSet.h" />
<ClInclude Include="..\..\modules\beast_core\containers\SparseSet.h" />
<ClInclude Include="..\..\modules\beast_core\containers\DynamicList.h" />
<ClInclude Include="..\..\modules\beast_core\containers\Variant.h" />
<ClInclude Include="..\..\modules\beast_core\diagnostic\FatalError.h" />
<ClInclude Include="..\..\modules\beast_core\diagnostic\FPUFlags.h" />
@@ -257,26 +259,17 @@
<ClInclude Include="..\..\modules\beast_core\maths\Random.h" />
<ClInclude Include="..\..\modules\beast_core\maths\Range.h" />
<ClInclude Include="..\..\modules\beast_core\maths\uint24.h" />
<ClInclude Include="..\..\modules\beast_core\memory\AllocatedBy.h" />
<ClInclude Include="..\..\modules\beast_core\memory\AtomicCounter.h" />
<ClInclude Include="..\..\modules\beast_core\memory\AtomicFlag.h" />
<ClInclude Include="..\..\modules\beast_core\memory\AtomicPointer.h" />
<ClInclude Include="..\..\modules\beast_core\memory\AtomicState.h" />
<ClInclude Include="..\..\modules\beast_core\memory\CacheLine.h" />
<ClInclude Include="..\..\modules\beast_core\memory\ContainerDeletePolicy.h" />
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStore.h" />
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStoreWithoutTLS.h" />
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStoreWithTLS.h" />
<ClInclude Include="..\..\modules\beast_core\memory\GlobalFifoFreeStore.h" />
<ClInclude Include="..\..\modules\beast_core\memory\GlobalPagedFreeStore.h" />
<ClInclude Include="..\..\modules\beast_core\memory\MemoryAlignment.h" />
<ClInclude Include="..\..\modules\beast_core\memory\MemoryBlock.h" />
<ClInclude Include="..\..\modules\beast_core\memory\OptionalScopedPointer.h" />
<ClInclude Include="..\..\modules\beast_core\memory\PagedFreeStore.h" />
<ClInclude Include="..\..\modules\beast_core\memory\RecycledObjectPool.h" />
<ClInclude Include="..\..\modules\beast_core\memory\SharedFunction.h" />
<ClInclude Include="..\..\modules\beast_core\memory\SharedObject.h" />
<ClInclude Include="..\..\modules\beast_core\memory\ScopedPointer.h" />
<ClInclude Include="..\..\modules\beast_core\memory\SharedSingleton.h" />
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h" />
<ClInclude Include="..\..\modules\beast_core\memory\SharedPtr.h" />
@@ -334,15 +327,10 @@
<ClInclude Include="..\..\modules\beast_core\threads\ThreadPool.h" />
<ClInclude Include="..\..\modules\beast_core\threads\TimeSliceThread.h" />
<ClInclude Include="..\..\modules\beast_core\threads\WaitableEvent.h" />
<ClInclude Include="..\..\modules\beast_core\thread\CallQueue.h" />
<ClInclude Include="..\..\modules\beast_core\thread\DeadlineTimer.h" />
<ClInclude Include="..\..\modules\beast_core\thread\InterruptibleThread.h" />
<ClInclude Include="..\..\modules\beast_core\thread\Listeners.h" />
<ClInclude Include="..\..\modules\beast_core\thread\ManualCallQueue.h" />
<ClInclude Include="..\..\modules\beast_core\thread\Semaphore.h" />
<ClInclude Include="..\..\modules\beast_core\thread\ServiceQueue.h" />
<ClInclude Include="..\..\modules\beast_core\thread\Stoppable.h" />
<ClInclude Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.h" />
<ClInclude Include="..\..\modules\beast_core\thread\Workers.h" />
<ClInclude Include="..\..\modules\beast_core\thread\detail\ScopedLock.h" />
<ClInclude Include="..\..\modules\beast_core\thread\detail\TrackedMutex.h" />
@@ -697,12 +685,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\containers\DynamicList.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\diagnostic\Assert.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -827,36 +809,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\FifoFreeStoreWithoutTLS.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\FifoFreeStoreWithTLS.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\GlobalPagedFreeStore.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\MemoryBlock.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\PagedFreeStore.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\StaticObject.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1175,36 +1133,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\CallQueue.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\DeadlineTimer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\InterruptibleThread.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Listeners.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\ManualCallQueue.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Semaphore.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1223,12 +1157,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Workers.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>

View File

@@ -285,6 +285,12 @@
<Filter Include="beast\config\stdlib">
<UniqueIdentifier>{7243e5e5-ad7e-4d81-8444-d545919e850c}</UniqueIdentifier>
</Filter>
<Filter Include="beast\asio">
<UniqueIdentifier>{549430fc-36f6-450e-9d8d-38f4b9e72fb5}</UniqueIdentifier>
</Filter>
<Filter Include="beast\smart_ptr">
<UniqueIdentifier>{4e9c54da-1581-41d7-ac75-48140e4a13d4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\modules\beast_core\beast_core.h">
@@ -377,9 +383,6 @@
<ClInclude Include="..\..\modules\beast_core\memory\OptionalScopedPointer.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\ScopedPointer.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
@@ -704,9 +707,6 @@
<ClInclude Include="..\..\modules\beast_core\maths\uint24.h">
<Filter>beast_core\maths</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\ContainerDeletePolicy.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\diagnostic\MeasureFunctionCallTime.h">
<Filter>beast_core\diagnostic</Filter>
</ClInclude>
@@ -737,51 +737,15 @@
<ClInclude Include="..\..\modules\beast_core\thread\DeadlineTimer.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\CallQueue.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\InterruptibleThread.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\Listeners.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\ManualCallQueue.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\Semaphore.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\thread\Workers.h">
<Filter>beast_core\thread</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\maths\Math.h">
<Filter>beast_core\maths</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\AllocatedBy.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStore.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStoreWithoutTLS.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\FifoFreeStoreWithTLS.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\GlobalFifoFreeStore.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\GlobalPagedFreeStore.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\memory\PagedFreeStore.h">
<Filter>beast_core\memory</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_asio\sockets\SocketWrapperStrand.h">
<Filter>beast_asio\sockets</Filter>
</ClInclude>
@@ -818,9 +782,6 @@
<ClInclude Include="..\..\modules\beast_core\system\SystemStats.h">
<Filter>beast_core\system</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_core\containers\DynamicList.h">
<Filter>beast_core\containers</Filter>
</ClInclude>
<ClInclude Include="..\..\beast\intrusive\ForwardList.h">
<Filter>beast\intrusive</Filter>
</ClInclude>
@@ -1245,6 +1206,15 @@
<ClInclude Include="..\..\beast\config\SelectStdlibConfig.h">
<Filter>beast\config</Filter>
</ClInclude>
<ClInclude Include="..\..\beast\smart_ptr\ContainerDeletePolicy.h">
<Filter>beast\smart_ptr</Filter>
</ClInclude>
<ClInclude Include="..\..\beast\smart_ptr\ScopedPointer.h">
<Filter>beast\smart_ptr</Filter>
</ClInclude>
<ClInclude Include="..\..\beast\SmartPtr.h">
<Filter>beast</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\beast_core\containers\AbstractFifo.cpp">
@@ -1580,39 +1550,12 @@
<ClCompile Include="..\..\modules\beast_core\thread\DeadlineTimer.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\CallQueue.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\InterruptibleThread.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Listeners.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\ManualCallQueue.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Semaphore.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\thread\Workers.cpp">
<Filter>beast_core\thread</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\FifoFreeStoreWithoutTLS.cpp">
<Filter>beast_core\memory</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\FifoFreeStoreWithTLS.cpp">
<Filter>beast_core\memory</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\GlobalPagedFreeStore.cpp">
<Filter>beast_core\memory</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\memory\PagedFreeStore.cpp">
<Filter>beast_core\memory</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_asio\basics\SSLContext.cpp">
<Filter>beast_asio\basics</Filter>
</ClCompile>
@@ -1622,9 +1565,6 @@
<ClCompile Include="..\..\modules\beast_core\system\SystemStats.cpp">
<Filter>beast_core\system</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\containers\DynamicList.cpp">
<Filter>beast_core\containers</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_asio\async\SharedHandler.cpp">
<Filter>beast_asio\async</Filter>
</ClCompile>

View File

@@ -17,27 +17,12 @@
*/
//==============================================================================
namespace
{
#ifndef BEAST_SMARTPTR_H_INCLUDED
#define BEAST_SMARTPTR_H_INCLUDED
// Size of a page
//
static const size_t globalPageBytes = 8 * 1024;
}
GlobalPagedFreeStore::GlobalPagedFreeStore ()
: m_allocator (globalPageBytes)
{
}
GlobalPagedFreeStore::~GlobalPagedFreeStore ()
{
}
GlobalPagedFreeStore::Ptr GlobalPagedFreeStore::getInstance ()
{
return SharedSingleton <GlobalPagedFreeStore>::getInstance();
}
#include "Config.h"
#include "smart_ptr/ContainerDeletePolicy.h"
#include "smart_ptr/ScopedPointer.h"
#endif

View File

@@ -17,8 +17,10 @@
*/
//==============================================================================
#ifndef BEAST_CONTAINERDELETEPOLICY_H_INCLUDED
#define BEAST_CONTAINERDELETEPOLICY_H_INCLUDED
#ifndef BEAST_SMARTPTR_CONTAINERDELETEPOLICY_H_INCLUDED
#define BEAST_SMARTPTR_CONTAINERDELETEPOLICY_H_INCLUDED
namespace beast {
/** The DeletePolicy provides a way to destroy objects stored in containers.
@@ -45,4 +47,6 @@ struct ContainerDeletePolicy
}
};
}
#endif

View File

@@ -21,8 +21,14 @@
*/
//==============================================================================
#ifndef BEAST_SCOPEDPOINTER_H_INCLUDED
#define BEAST_SCOPEDPOINTER_H_INCLUDED
#ifndef BEAST_SMARTPTR_SCOPEDPOINTER_H_INCLUDED
#define BEAST_SMARTPTR_SCOPEDPOINTER_H_INCLUDED
#include "ContainerDeletePolicy.h"
#include "../Uncopyable.h"
#include "../StaticAssert.h"
namespace beast {
//==============================================================================
/**
@@ -248,4 +254,7 @@ template <typename Type>
void deleteAndZero (ScopedPointer<Type>&) { static_bassert (sizeof (Type) == 12345); }
#endif
#endif // BEAST_SCOPEDPOINTER_H_INCLUDED
}
#endif

View File

@@ -39,7 +39,6 @@ namespace beast {
#include "protocol/HandshakeDetectLogicPROXY.cpp"
# include "http/HTTPParserImpl.h"
#include "http/HTTPParser.cpp"
#include "http/HTTPClientType.cpp"
#include "http/HTTPField.cpp"
#include "http/HTTPHeaders.cpp"
@@ -61,3 +60,5 @@ namespace beast {
#include "system/BoostUnitTests.cpp"
}
#include "http/HTTPParser.cpp"

View File

@@ -89,10 +89,11 @@ namespace beast {
# include "http/HTTPMessage.h"
# include "http/HTTPRequest.h"
# include "http/HTTPResponse.h"
# include "http/HTTPParser.h"
}
# include "http/HTTPParser.h"
#include "http/HTTPClientType.h"
namespace beast {

View File

@@ -17,6 +17,8 @@
*/
//==============================================================================
namespace beast {
HTTPParser::HTTPParser (Type type)
: m_type (type)
, m_impl (new HTTPParserImpl (
@@ -102,3 +104,5 @@ SharedPtr <HTTPResponse> const& HTTPParser::response ()
return m_response;
}
}

View File

@@ -20,6 +20,8 @@
#ifndef BEAST_ASIO_HTTPPARSER_H_INCLUDED
#define BEAST_ASIO_HTTPPARSER_H_INCLUDED
namespace beast {
class HTTPParserImpl;
/** A parser for HTTPRequest and HTTPResponse objects. */
@@ -82,4 +84,6 @@ private:
SharedPtr <HTTPResponse> m_response;
};
}
#endif

View File

@@ -134,7 +134,6 @@ namespace beast
#include "containers/NamedValueSet.cpp"
#include "containers/PropertySet.cpp"
#include "containers/Variant.cpp"
#include "containers/DynamicList.cpp"
#include "diagnostic/FatalError.cpp"
#include "diagnostic/FPUFlags.cpp"
@@ -162,10 +161,6 @@ namespace beast
#include "maths/Random.cpp"
#include "memory/MemoryBlock.cpp"
#include "memory/FifoFreeStoreWithTLS.cpp"
#include "memory/FifoFreeStoreWithoutTLS.cpp"
#include "memory/GlobalPagedFreeStore.cpp"
#include "memory/PagedFreeStore.cpp"
#include "memory/StaticObject.cpp"
#include "misc/Main.cpp"
@@ -197,13 +192,8 @@ namespace beast
#include "thread/impl/TrackedMutex.cpp"
#include "thread/DeadlineTimer.cpp"
#include "thread/InterruptibleThread.cpp"
#include "thread/Stoppable.cpp"
#include "thread/Semaphore.cpp"
#include "thread/CallQueue.cpp"
#include "thread/Listeners.cpp"
#include "thread/ManualCallQueue.cpp"
#include "thread/ThreadWithCallQueue.cpp"
#include "thread/Workers.cpp"
#include "threads/ChildProcess.cpp"

View File

@@ -45,6 +45,7 @@
// New header-only library modeled more closely according to boost
#include "../../beast/CStdInt.h"
#include "../../beast/SmartPtr.h"
#include "../../beast/StaticAssert.h"
#include "../../beast/Uncopyable.h"
#include "../../beast/Atomic.h"
@@ -155,7 +156,6 @@ class FileOutputStream;
#include "thread/TrackedMutex.h"
#include "diagnostic/FatalError.h"
#include "text/LexicalCast.h"
#include "memory/ContainerDeletePolicy.h"
#include "maths/Math.h"
#include "maths/uint24.h"
#include "logging/Logger.h"
@@ -180,8 +180,6 @@ class FileOutputStream;
#include "containers/SortedSet.h"
#include "maths/Range.h"
#include "containers/SparseSet.h"
# include "containers/DynamicList.h"
#include "memory/ScopedPointer.h"
#include "files/DirectoryIterator.h"
#include "streams/InputStream.h"
#include "files/FileInputStream.h"
@@ -242,21 +240,8 @@ class FileOutputStream;
#include "thread/DeadlineTimer.h"
#include "memory/AllocatedBy.h"
#include "memory/PagedFreeStore.h"
#include "memory/GlobalPagedFreeStore.h"
#include "memory/FifoFreeStoreWithTLS.h"
#include "memory/FifoFreeStoreWithoutTLS.h"
#include "memory/FifoFreeStore.h"
#include "memory/GlobalFifoFreeStore.h"
#include "thread/Semaphore.h"
#include "thread/InterruptibleThread.h"
#include "thread/Stoppable.h"
#include "thread/CallQueue.h"
#include "thread/Listeners.h"
#include "thread/ManualCallQueue.h"
#include "thread/ThreadWithCallQueue.h"
#include "thread/Workers.h"
}

View File

@@ -1,327 +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 ContainerTests
{
//------------------------------------------------------------------------------
/** Counts the number of occurrences of each type of operation. */
class Counts
{
public:
typedef std::size_t count_type;
Counts ()
: default_ctor (0)
, copy_ctor (0)
, copy_assign (0)
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
, move_ctor (0)
, move_assign (0)
#endif
{
}
Counts (Counts const& other)
: default_ctor (other.default_ctor)
, copy_ctor (other.copy_ctor)
, copy_assign (other.copy_assign)
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
, move_ctor (other.move_ctor)
, move_assign (other.move_assign)
#endif
{
}
Counts& operator= (Counts const& other)
{
default_ctor = other.default_ctor;
copy_ctor = other.copy_ctor;
copy_assign = other.copy_assign;
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
move_ctor = other.move_ctor;
move_assign = other.move_assign;
#endif
return *this;
}
friend inline Counts operator- (Counts const& lhs, Counts const& rhs)
{
Counts result;
result.default_ctor = lhs.default_ctor - rhs.default_ctor;
result.copy_ctor = lhs.copy_ctor - rhs.copy_ctor;
result.copy_assign = lhs.copy_assign - rhs.copy_assign;
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
result.move_ctor = lhs.move_ctor - rhs.move_ctor;
result.move_assign = lhs.move_assign - rhs.move_assign;
#endif
return result;
}
String toString () const
{
return String()
+ "default_ctor(" + String::fromNumber (default_ctor) + ") "
+ ", copy_ctor(" + String::fromNumber (copy_ctor) + ")"
+ ", copy_assign(" + String::fromNumber (copy_assign) + ")"
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
+ ", move_ctor(" + String::fromNumber (move_ctor) + ")"
+ ", move_assign(" + String::fromNumber (move_assign) + ")"
#endif
;
}
count_type default_ctor;
count_type copy_ctor;
count_type copy_assign;
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
count_type move_ctor;
count_type move_assign;
#endif
};
//------------------------------------------------------------------------------
/** Counts the number of element operations performed in a scope. */
class ScopedCounts : public Uncopyable
{
public:
ScopedCounts (Counts const& counts)
: m_start (counts)
, m_counts (counts)
{
}
Counts get () const
{
return m_counts - m_start;
}
private:
Counts const m_start;
Counts const& m_counts;
};
//------------------------------------------------------------------------------
/* Base for element configurations. */
class ElementConfigBase : public Uncopyable
{
public:
typedef std::size_t IdType;
};
/** Provides the element-specific configuration members. */
template <class Params>
class ElementConfig : public ElementConfigBase
{
public:
static Counts& getCounts ()
{
static Counts counts;
return counts;
}
};
//------------------------------------------------------------------------------
/** Base for elements used in container unit tests. */
class ElementBase
{
public:
};
/** An object placed into a container for unit testing. */
template <class Config>
class Element : public ElementBase
{
public:
typedef typename Config::IdType IdType;
Element ()
: m_id (0)
, m_msg (String::empty)
{
++Config::getCounts ().default_ctor;
}
explicit Element (IdType id)
: m_id (id)
, m_msg (String::fromNumber (id))
{
}
Element (Element const& other)
: m_id (other.m_id)
, m_msg (other.m_msg)
{
++Config::getCounts ().copy_ctor;
}
Element& operator= (Element const& other)
{
m_id = other.m_id;
m_msg = other.m_msg;
++Config::getCounts ().copy_assign;
}
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
Element (Element&& other)
: m_id (other.m_id)
, m_msg (other.m_msg)
{
other.m_msg = String::empty;
++Config::getCounts ().move_ctor;
}
Element& operator= (Element&& other)
{
m_id = other.m_id;
m_msg = other.m_msg;
other.m_msg = String::empty;
++Config::getCounts ().move_assign;
}
#endif
IdType id () const
{
return m_id;
}
String msg () const
{
return m_msg;
}
private:
IdType m_id;
String m_msg;
};
//------------------------------------------------------------------------------
/** Base for test state parameters. */
class StateBase : public Uncopyable
{
public:
typedef int64 SeedType;
};
/** Provides configuration-specific test state parameters. */
template <class Params>
class State : public StateBase
{
public:
static Random& random ()
{
static Random generator (Params::seedValue);
return generator;
}
};
//------------------------------------------------------------------------------
class ConfigBase
{
};
template <template <typename, class> class Container,
class Params>
class Config
: public State <Params>
, public ElementConfig <Params>
{
public:
typedef Config ConfigType;
typedef Params ParamsType;
typedef State <Params> StateType;
typedef ElementConfig <Params> ElementConfigType;
typedef Element <ElementConfigType> ElementType;
typedef Container <
ElementType,
std::allocator <char> > ContainerType;
typedef StateBase::SeedType SeedType;
// default seed
static SeedType const seedValue = 69;
};
//------------------------------------------------------------------------------
/** A generic container test. */
template <class Params>
class Test : public Params
{
public:
typedef typename Params::StateType StateType;
typedef typename Params::ElementConfigType ElementConfigType;
typedef typename Params::ContainerType ContainerType;
void doInsert ()
{
m_container.reserve (Params::elementCount);
for (typename ContainerType::size_type i = 0;
i < Params::elementCount; ++i)
{
m_container.push_back (typename Params::ElementType ());
}
}
private:
typename Params::ContainerType m_container;
};
//------------------------------------------------------------------------------
class DynamicListTests : public UnitTest
{
public:
struct Params : Config <DynamicList, Params>
{
static SeedType const seedValue = 42;
static ContainerType::size_type const elementCount = 100 * 1000;
};
typedef Test <Params> TestType;
void runTest ()
{
TestType test;
beginTestCase ("insert");
{
ScopedCounts counts (TestType::getCounts ());
test.doInsert ();
this->logMessage (counts.get ().toString ());
}
pass ();
}
DynamicListTests () : UnitTest ("DynamicList", "beast")
{
}
};
static DynamicListTests dynamicListTests;
}

View File

@@ -1,473 +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_CORE_CONTAINERS_DYNAMICLIST_H_INCLUDED
#define BEAST_CORE_CONTAINERS_DYNAMICLIST_H_INCLUDED
template <typename, typename>
class DynamicList;
template <class Container, bool IsConst>
class DynamicListIterator
: public std::iterator <
std::bidirectional_iterator_tag,
typename Container::value_type,
typename Container::difference_type,
typename mpl::IfCond <IsConst,
typename Container::const_pointer,
typename Container::pointer>::type,
typename mpl::IfCond <IsConst,
typename Container::const_reference,
typename Container::reference>::type>
{
private:
typedef typename mpl::IfCond <IsConst,
typename List <typename Container::Item>::const_iterator,
typename List <typename Container::Item>::iterator>::type iterator_type;
typedef typename mpl::IfCond <IsConst,
typename Container::const_pointer,
typename Container::pointer>::type pointer;
typedef typename mpl::IfCond <IsConst,
typename Container::const_reference,
typename Container::reference>::type reference;
public:
DynamicListIterator ()
{
}
DynamicListIterator (iterator_type iter)
: m_iter (iter)
{
}
DynamicListIterator (DynamicListIterator const& other)
: m_iter (other.m_iter)
{
}
template <bool OtherIsConst>
DynamicListIterator (DynamicListIterator <Container, OtherIsConst> const& other)
: m_iter (other.m_iter)
{
}
DynamicListIterator& operator= (DynamicListIterator const& other)
{
m_iter = other.m_iter;
return *this;
}
template <bool OtherIsConst>
DynamicListIterator& operator= (DynamicListIterator <
Container, OtherIsConst> const& other)
{
m_iter = other.m_iter;
return *this;
}
template <bool OtherIsConst>
bool operator== (DynamicListIterator <Container, OtherIsConst> const& other) const
{
return m_iter == other.m_iter;
}
template <bool OtherIsConst>
bool operator!= (DynamicListIterator <Container, OtherIsConst> const& other) const
{
return ! ((*this) == other);
}
reference operator* () const
{
return dereference ();
}
pointer operator-> () const
{
return &dereference ();
}
DynamicListIterator& operator++ ()
{
increment ();
return *this;
}
DynamicListIterator operator++ (int)
{
DynamicListIterator const result (*this);
increment ();
return result;
}
DynamicListIterator& operator-- ()
{
decrement ();
return *this;
}
DynamicListIterator operator-- (int)
{
DynamicListIterator const result (*this);
decrement ();
return result;
}
private:
template <typename, typename>
friend class DynamicList;
reference dereference () const
{
return *(m_iter->get ());
}
void increment ()
{
++m_iter;
}
void decrement ()
{
--m_iter;
}
iterator_type m_iter;
};
//------------------------------------------------------------------------------
/** A list that uses a very small number of dynamic allocations.
Once an element is allocated, its address does not change. Elements
can be erased, and they are placed onto a deleted list for re-use.
Allocations occur in configurable batches.
Iterators to elements never become invalid, they can be safely
stored elsewhere, as long as the underlying element is not erased.
T may support these concepts:
DefaultConstructible
MoveConstructible (C++11)
T must support these concepts:
Destructible
*/
template <typename T,
class Allocator = std::allocator <char> >
class DynamicList
{
private:
typedef PARAMETER_TYPE (T) TParam;
public:
enum
{
defaultBlocksize = 1000
};
typedef T value_type;
typedef Allocator allocator_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef T const* const_pointer;
typedef T const& const_reference;
typedef DynamicList <
T,
Allocator> container_type;
typedef DynamicListIterator <container_type, false> iterator;
typedef DynamicListIterator <container_type, true> const_iterator;
public:
explicit DynamicList (
size_type blocksize = defaultBlocksize,
Allocator const& allocator = Allocator ())
: m_allocator (allocator)
, m_blocksize (blocksize)
, m_capacity (0)
{
}
~DynamicList ()
{
clear ();
shrink_to_fit ();
}
allocator_type get_allocator () const noexcept
{
return m_allocator;
}
//--------------------------------------------------------------------------
reference front () noexcept
{
return *m_items.front ().get ();
}
const_reference front () const noexcept
{
return *m_items.front ().get ();
}
reference back () noexcept
{
return *m_items.back ().get ();
}
const_reference back () const noexcept
{
return *m_items.back ().get ();
}
//--------------------------------------------------------------------------
iterator begin () noexcept
{
return iterator (m_items.begin ());
}
const_iterator begin () const noexcept
{
return const_iterator (m_items.begin ());
}
const_iterator cbegin () const noexcept
{
return const_iterator (m_items.cbegin ());
}
iterator end () noexcept
{
return iterator (m_items.end ());
}
const_iterator end () const noexcept
{
return const_iterator (m_items.end ());
}
const_iterator cend () const noexcept
{
return const_iterator (m_items.cend ());
}
iterator iterator_to (T& value) noexcept
{
std::ptrdiff_t const offset (
(std::ptrdiff_t)(((Item const*)0)->get ()));
Item& item (*addBytesToPointer (((Item*)&value), -offset));
return iterator (m_items.iterator_to (item));
}
const_iterator const_iterator_to (T const& value) const noexcept
{
std::ptrdiff_t const offset (
(std::ptrdiff_t)(((Item const*)0)->get ()));
Item const& item (*addBytesToPointer (((Item const*)&value), -offset));
return const_iterator (m_items.const_iterator_to (item));
}
//--------------------------------------------------------------------------
bool empty () const noexcept
{
return m_items.empty ();
}
size_type size () const noexcept
{
return m_items.size ();
}
size_type max_size () const noexcept
{
return std::numeric_limits <size_type>::max ();
}
void reserve (size_type new_cap) noexcept
{
new_cap = m_blocksize * (
(new_cap + m_blocksize - 1) / m_blocksize);
if (new_cap > max_size ())
Throw (std::length_error ("new_cap > max_size"), __FILE__, __LINE__);
if (new_cap <= m_capacity)
return;
size_type const n (new_cap / m_blocksize);
m_handles.reserve (n);
for (size_type i = m_handles.size (); i < n; ++i)
m_handles.push_back (static_cast <Item*> (std::malloc (
m_blocksize * sizeof (Item))));
m_capacity = new_cap;
}
size_type capacity () const noexcept
{
return m_capacity;
}
void shrink_to_fit ()
{
// Special case when all allocated
// items are part of the free list.
if (m_items.empty ())
m_free.clear ();
size_type const used (m_items.size () + m_free.size ());
size_type const handles ((used + m_blocksize - 1) / m_blocksize);
m_capacity = handles * m_blocksize;
for (size_type i = m_handles.size (); i-- > handles;)
{
std::free (m_handles [i]);
m_handles.erase (m_handles.begin () + i);
}
}
//--------------------------------------------------------------------------
void clear ()
{
// Might want to skip this if is_pod<T> is true
for (typename List <Item>::iterator iter = m_items.begin ();
iter != m_items.end ();)
{
Item& item (*iter++);
item.get ()->~T ();
m_free.push_back (item);
}
}
/** Allocate a new default-constructed element and return the iterator.
If there are deleted elements in the free list, the new element
may not be created at the end of the storage area.
*/
iterator emplace_back ()
{
return iterator_to (*new (alloc ()->get ()) T ());
}
template <class A1>
iterator emplace_back (A1 a1)
{
return iterator_to (*new (alloc ()->get ()) T (a1));
}
template <class A1, class A2>
iterator emplace_back (A1 a1, A2 a2)
{
return iterator_to (*new (alloc ()->get ()) T (a1, a2));
}
template <class A1, class A2, class A3>
iterator emplace_back (A1 a1, A2 a2, A3 a3)
{
return iterator_to (*new (alloc ()->get ()) T (a1, a2, a3));
}
template <class A1, class A2, class A3, class A4>
iterator emplace_back (A1 a1, A2 a2, A3 a3, A4 a4)
{
return iterator_to (*new (alloc ()->get ()) T (a1, a2, a3, a4));
}
template <class A1, class A2, class A3, class A4, class A5>
iterator emplace_back (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
{
return iterator_to (*new (alloc ()->get ()) T (a1, a2, a3, a4, a5));
}
/** Allocate a new copy-constructed element and return the index. */
iterator push_back (TParam value) noexcept
{
return iterator_to (*new (alloc ()->get ()) T (value));
}
/** Erase the element at the specified position. */
iterator erase (iterator pos)
{
Item& item (*pos.m_iter);
item.get ()->~T ();
pos = m_items.erase (m_items.iterator_to (item));
m_free.push_front (item);
return pos;
}
private:
template <class, bool>
friend class DynamicListIterator;
struct Item : List <Item>::Node
{
typedef T value_type;
T* get () noexcept
{
return reinterpret_cast <T*> (&storage [0]);
}
T const* get () const noexcept
{
return reinterpret_cast <T const*> (&storage [0]);
}
private:
// Lets hope this is padded correctly
uint8 storage [sizeof (T)];
};
Item* alloc () noexcept
{
Item* item;
if (m_free.empty ())
{
if (m_capacity <= m_items.size ())
reserve (m_items.size () + 1);
size_type const index (m_items.size () / m_blocksize);
size_type const offset (m_items.size () - index * m_blocksize);
item = m_handles [index] + offset;
}
else
{
item = &m_free.pop_front ();
}
m_items.push_back (*item);
return item;
}
typedef std::vector <Item*> blocks_t;
Allocator m_allocator;
size_type m_blocksize;
size_type m_capacity;
std::vector <Item*> m_handles;
List <Item> m_items;
List <Item> m_free;
};
#endif

View File

@@ -1,63 +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_ALLOCATEDBY_H_INCLUDED
#define BEAST_ALLOCATEDBY_H_INCLUDED
/*============================================================================*/
/**
Customized allocation for heap objects.
Derived classes will use the specified allocator for new and delete.
@param AllocatorType The type of allocator to use.
@ingroup beast_concurrent
*/
template <class AllocatorType>
class AllocatedBy
{
public:
static inline void* operator new (size_t bytes, AllocatorType& allocator) noexcept
{
return allocator.allocate (bytes);
}
static inline void* operator new (size_t bytes, AllocatorType* allocator) noexcept
{
return allocator->allocate (bytes);
}
static inline void operator delete (void* p, AllocatorType&) noexcept
{
AllocatorType::deallocate (p);
}
static inline void operator delete (void* p, AllocatorType*) noexcept
{
AllocatorType::deallocate (p);
}
static inline void operator delete (void* p) noexcept
{
AllocatorType::deallocate (p);
}
};
#endif

View File

@@ -1,38 +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_FIFOFREESTORE_H_INCLUDED
#define BEAST_FIFOFREESTORE_H_INCLUDED
/** Selected free store based on compilation settings.
@ingroup beast_concurrent
*/
// VFALCO NOTE Disabled this because it seems that the TLS
// implementation has a leak. Although the other
// one also seems to have a leak.
//
//#if BEAST_USE_BOOST_FEATURES
#if 0
typedef FifoFreeStoreWithTLS FifoFreeStoreType;
#else
typedef FifoFreeStoreWithoutTLS FifoFreeStoreType;
#endif
#endif

View File

@@ -1,198 +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.
*/
//==============================================================================
// Implementation notes
//
// - A Page is a large allocation from a global PageAllocator.
//
// - Each thread maintains an 'active' page from which it makes allocations.
//
// - When the active page is full, a new one takes it's place.
//
// - Page memory is deallocated when it is not active and no longer referenced.
//
// - Each instance of FifoFreeStoreWithTLS maintains its own set of per-thread active pages,
// but uses a global PageAllocator. This reduces memory consumption without
// affecting performance.
//
#if BEAST_USE_BOOST_FEATURES
// This precedes every allocation
//
struct FifoFreeStoreWithTLS::Header
{
FifoFreeStoreWithTLS::Page* page;
};
//------------------------------------------------------------------------------
class FifoFreeStoreWithTLS::Page : LeakChecked <Page>, public Uncopyable
{
public:
explicit Page (const size_t bytes) : m_refs (1)
{
m_end = reinterpret_cast <char*> (this) + bytes;
m_free = reinterpret_cast <char*> (
Memory::pointerAdjustedForAlignment (this + 1));
}
~Page ()
{
bassert (! m_refs.isSignaled ());
}
inline bool release ()
{
bassert (! m_refs.isSignaled ());
return m_refs.release ();
}
void* allocate (size_t bytes)
{
bassert (bytes > 0);
char* p = Memory::pointerAdjustedForAlignment (m_free);
char* free = p + bytes;
if (free <= m_end)
{
m_free = free;
m_refs.addref ();
}
else
{
p = 0;
}
return p;
}
private:
AtomicCounter m_refs; // reference count
char* m_free; // next free byte
char* m_end; // last free byte + 1
};
//------------------------------------------------------------------------------
class FifoFreeStoreWithTLS::PerThreadData : LeakChecked <PerThreadData>, public Uncopyable
{
public:
explicit PerThreadData (FifoFreeStoreWithTLS* allocator)
: m_allocator (*allocator)
, m_active (m_allocator.newPage ())
{
}
~PerThreadData ()
{
if (m_active->release ())
m_allocator.deletePage (m_active);
}
inline void* allocate (const size_t bytes)
{
const size_t headerBytes = Memory::sizeAdjustedForAlignment (sizeof (Header));
const size_t bytesNeeded = headerBytes + bytes;
if (bytesNeeded > m_allocator.m_pages->getPageBytes ())
fatal_error ("the memory request was too large");
Header* header;
header = reinterpret_cast <Header*> (m_active->allocate (bytesNeeded));
if (!header)
{
if (m_active->release ())
deletePage (m_active);
m_active = m_allocator.newPage ();
header = reinterpret_cast <Header*> (m_active->allocate (bytesNeeded));
}
header->page = m_active;
return reinterpret_cast <char*> (header) + headerBytes;
}
private:
FifoFreeStoreWithTLS& m_allocator;
Page* m_active;
};
//------------------------------------------------------------------------------
inline FifoFreeStoreWithTLS::Page* FifoFreeStoreWithTLS::newPage ()
{
return new (m_pages->allocate ()) Page (m_pages->getPageBytes ());
}
inline void FifoFreeStoreWithTLS::deletePage (Page* page)
{
// Safe, because each thread maintains its own active page.
page->~Page ();
PagedFreeStoreType::deallocate (page);
}
FifoFreeStoreWithTLS::FifoFreeStoreWithTLS ()
: m_pages (PagedFreeStoreType::getInstance ())
{
//bassert (m_pages->getPageBytes () >= sizeof (Page) + Memory::allocAlignBytes);
}
FifoFreeStoreWithTLS::~FifoFreeStoreWithTLS ()
{
// Clean up this thread's data before we release
// the reference to the global page allocator.
m_tsp.reset (0);
}
//------------------------------------------------------------------------------
void* FifoFreeStoreWithTLS::allocate (const size_t bytes)
{
PerThreadData* data = m_tsp.get ();
if (!data)
{
data = new PerThreadData (this);
m_tsp.reset (data);
}
return data->allocate (bytes);
}
//------------------------------------------------------------------------------
void FifoFreeStoreWithTLS::deallocate (void* p)
{
const size_t headerBytes = Memory::sizeAdjustedForAlignment (sizeof (Header));
Header* const header = reinterpret_cast <Header*> (reinterpret_cast <char*> (p) - headerBytes);
Page* const page = header->page;
if (page->release ())
deletePage (page);
}
#endif

View File

@@ -1,69 +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_FIFOFREESTOREWITHTLS_H_INCLUDED
#define BEAST_FIFOFREESTOREWITHTLS_H_INCLUDED
#if BEAST_USE_BOOST_FEATURES
/*============================================================================*/
/**
Lock-free and mostly wait-free FIFO memory allocator.
This allocator is suitable for use with CallQueue and Listeners. It is
expected that over time, deallocations will occur in roughly the same order
as allocations.
@note This implementation uses Thread Local Storage to further improve
performance. However, it requires boost style thread_specific_ptr.
@invariant allocate() and deallocate() are fully concurrent.
@invariant The ABA problem is handled automatically.
@ingroup beast_concurrent
*/
class BEAST_API FifoFreeStoreWithTLS : public LeakChecked <FifoFreeStoreWithTLS>
{
public:
FifoFreeStoreWithTLS ();
~FifoFreeStoreWithTLS ();
void* allocate (const size_t bytes);
static void deallocate (void* const p);
private:
typedef GlobalPagedFreeStore PagedFreeStoreType;
struct Header;
class Page;
inline Page* newPage ();
static inline void deletePage (Page* page);
private:
class PerThreadData;
boost::thread_specific_ptr <PerThreadData> m_tsp;
PagedFreeStoreType::Ptr m_pages;
};
#endif
#endif

View File

@@ -1,241 +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.
*/
//==============================================================================
// This precedes every allocation
struct FifoFreeStoreWithoutTLS::Header
{
union
{
FifoFreeStoreWithoutTLS::Block* block; // backpointer to the page
char pad [Memory::allocAlignBytes];
};
};
//------------------------------------------------------------------------------
class FifoFreeStoreWithoutTLS::Block : public Uncopyable
{
public:
explicit Block (const size_t bytes) : m_refs (1)
{
m_end = reinterpret_cast <char*> (this) + bytes;
m_free = reinterpret_cast <char*> (
Memory::pointerAdjustedForAlignment (this + 1));
}
~Block ()
{
bassert (!m_refs.isSignaled ());
}
inline void addref ()
{
m_refs.addref ();
}
inline bool release ()
{
return m_refs.release ();
}
enum Result
{
success, // successful allocation
ignore, // disregard the block
consumed // block is consumed (1 thread sees this)
};
Result allocate (size_t bytes, void* pBlock)
{
bassert (bytes > 0);
Result result;
for (;;)
{
char* base = m_free.get ();
if (base)
{
char* p = Memory::pointerAdjustedForAlignment (base);
char* free = p + bytes;
if (free <= m_end)
{
// Try to commit the allocation
if (m_free.compareAndSet (free, base))
{
* (reinterpret_cast <void**> (pBlock)) = p;
result = success;
break;
}
else
{
// Someone changed m_free, retry.
}
}
else
{
// Mark the block consumed.
if (m_free.compareAndSet (0, base))
{
// Only one caller sees this, the rest get 'ignore'
result = consumed;
break;
}
else
{
// Happens with another concurrent allocate(), retry.
}
}
}
else
{
// Block is consumed, ignore it.
result = ignore;
break;
}
}
return result;
}
private:
AtomicCounter m_refs; // reference count
AtomicPointer <char> m_free; // next free byte or 0 if inactive.
char* m_end; // last free byte + 1
};
//------------------------------------------------------------------------------
inline FifoFreeStoreWithoutTLS::Block* FifoFreeStoreWithoutTLS::newBlock ()
{
return new (m_pages->allocate ()) Block (m_pages->getPageBytes ());
}
inline void FifoFreeStoreWithoutTLS::deleteBlock (Block* b)
{
// It is critical that we do not call the destructor,
// because due to the lock-free implementation, a Block
// can be accessed for a short time after it is deleted.
/* b->~Block (); */ // DO NOT CALL!!!
PagedFreeStoreType::deallocate (b);
}
FifoFreeStoreWithoutTLS::FifoFreeStoreWithoutTLS ()
: m_pages (GlobalPagedFreeStore::getInstance ())
{
if (m_pages->getPageBytes () < sizeof (Block) + 256)
fatal_error ("the block size is too small");
m_active = newBlock ();
}
FifoFreeStoreWithoutTLS::~FifoFreeStoreWithoutTLS ()
{
deleteBlock (m_active);
}
//------------------------------------------------------------------------------
void* FifoFreeStoreWithoutTLS::allocate (const size_t bytes)
{
const size_t actual = sizeof (Header) + bytes;
if (actual > m_pages->getPageBytes ())
fatal_error ("the memory request was too large");
Header* h;
for (;;)
{
// Get an active block.
Block* b = m_active;
while (!b)
{
Thread::yield ();
b = m_active;
}
// (*) It is possible for the block to get a final release here
// In this case it will have been put in the garbage, and
// m_active will not match.
// Acquire a reference.
b->addref ();
// Is it still active?
if (m_active == b)
{
// Yes so try to allocate from it.
const Block::Result result = b->allocate (actual, &h);
if (result == Block::success)
{
// Keep the reference and return the allocation.
h->block = b;
break;
}
else if (result == Block::consumed)
{
// Remove block from active.
m_active = 0;
// Take away the reference we added
b->release ();
// Take away the original active reference.
if (b->release ())
deleteBlock (b);
// Install a fresh empty active block.
m_active = newBlock ();
}
else
{
if (b->release ())
deleteBlock (b);
}
// Try again.
}
else
{
// Block became inactive, so release our reference.
b->release ();
// (*) It is possible for this to be a duplicate final release.
}
}
return h + 1;
}
//------------------------------------------------------------------------------
void FifoFreeStoreWithoutTLS::deallocate (void* p)
{
Block* const b = (reinterpret_cast <Header*> (p) - 1)->block;
if (b->release ())
deleteBlock (b);
}

View File

@@ -1,65 +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_FIFOFREESTOREWITHOUTTLS_H_INCLUDED
#define BEAST_FIFOFREESTOREWITHOUTTLS_H_INCLUDED
/*============================================================================*/
/**
Lock-free FIFO memory allocator.
This allocator is suitable for use with CallQueue and Listeners. It is
expected that over time, deallocations will occur in roughly the same order
as allocations.
@note This version of the fifo free store uses less memory and doesn't require
thread specific storage. However, it runs slower. The performance
differences are negligible for desktop class applications.
@invariant allocate() and deallocate() are fully concurrent.
@invariant The ABA problem is handled automatically.
@ingroup beast_concurrent
*/
class BEAST_API FifoFreeStoreWithoutTLS : LeakChecked <FifoFreeStoreWithoutTLS>
{
public:
explicit FifoFreeStoreWithoutTLS ();
~FifoFreeStoreWithoutTLS ();
void* allocate (const size_t bytes);
static void deallocate (void* const p);
private:
typedef GlobalPagedFreeStore PagedFreeStoreType;
struct Header;
class Block;
inline Block* newBlock ();
static inline void deleteBlock (Block* b);
private:
Block* volatile m_active;
PagedFreeStoreType::Ptr m_pages;
};
#endif

View File

@@ -1,63 +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_GLOBALFIFOFREESTORE_H_INCLUDED
#define BEAST_GLOBALFIFOFREESTORE_H_INCLUDED
/*============================================================================*/
/**
A @ref FifoFreeStoreType singleton.
@ingroup beast_concurrent
*/
template <class Tag>
class GlobalFifoFreeStore
{
public:
inline void* allocate (size_t bytes)
{
return m_allocator.allocate (bytes);
}
static inline void deallocate (void* const p)
{
FifoFreeStoreType::deallocate (p);
}
typedef SharedPtr <SharedSingleton <GlobalFifoFreeStore> > Ptr;
static Ptr getInstance ()
{
return SharedSingleton <GlobalFifoFreeStore>::getInstance();
}
public:
GlobalFifoFreeStore ()
{
}
~GlobalFifoFreeStore ()
{
}
private:
FifoFreeStoreType m_allocator;
};
#endif

View File

@@ -1,59 +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_GLOBALPAGEDFREESTORE_H_INCLUDED
#define BEAST_GLOBALPAGEDFREESTORE_H_INCLUDED
/*============================================================================*/
/**
A PagedFreeStore singleton.
@ingroup beast_concurrent
*/
class BEAST_API GlobalPagedFreeStore : public LeakChecked <GlobalPagedFreeStore>
{
public:
GlobalPagedFreeStore ();
~GlobalPagedFreeStore ();
public:
inline size_t getPageBytes ()
{
return m_allocator.getPageBytes ();
}
inline void* allocate ()
{
return m_allocator.allocate ();
}
static inline void deallocate (void* const p)
{
PagedFreeStore::deallocate (p);
}
typedef SharedPtr <SharedSingleton <GlobalPagedFreeStore> > Ptr;
static Ptr getInstance ();
private:
PagedFreeStore m_allocator;
};
#endif

View File

@@ -1,227 +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.
*/
//==============================================================================
#define LOG_GC 0
namespace
{
// This is the upper limit on the amount of physical memory an instance of the
// allocator will allow. Going over this limit means that consumers cannot keep
// up with producers, and application logic should be re-examined.
//
// TODO: ENFORCE THIS GLOBALLY? MEASURE IN KILOBYTES AND FORCE KILOBYTE PAGE SIZES
#define HARD_LIMIT 1
const size_t hardLimitMegaBytes = 256;
}
/*
Implementation notes
- There are two pools, the 'hot' pool and the 'cold' pool.
- When a new page is needed we pop from the 'fresh' stack of the hot pool.
- When a page is deallocated it is pushed to the 'garbage' stack of the hot pool.
- Every so often, a garbage collection is performed on a separate thread.
During collection, fresh and garbage are swapped in the cold pool.
Then, the hot and cold pools are atomically swapped.
*/
//------------------------------------------------------------------------------
struct PagedFreeStore::Page : Pages::Node, LeakChecked <Page>
{
explicit Page (PagedFreeStore* const allocator)
: m_allocator (*allocator)
{
}
PagedFreeStore& getAllocator () const
{
return m_allocator;
}
private:
PagedFreeStore& m_allocator;
};
inline void* PagedFreeStore::fromPage (Page* const p)
{
return reinterpret_cast <char*> (p) +
Memory::sizeAdjustedForAlignment (sizeof (Page));
}
inline PagedFreeStore::Page* PagedFreeStore::toPage (void* const p)
{
return reinterpret_cast <Page*> (
(reinterpret_cast <char*> (p) -
Memory::sizeAdjustedForAlignment (sizeof (Page))));
}
//------------------------------------------------------------------------------
PagedFreeStore::PagedFreeStore (const size_t pageBytes)
: m_timer (this)
, m_pageBytes (pageBytes)
, m_pageBytesAvailable (pageBytes - Memory::sizeAdjustedForAlignment (sizeof (Page)))
, m_newPagesLeft (int ((hardLimitMegaBytes * 1024 * 1024) / m_pageBytes))
#if LOG_GC
, m_swaps (0)
#endif
{
m_hot = m_pool1;
m_cold = m_pool2;
m_timer.setExpiration (1.0);
}
PagedFreeStore::~PagedFreeStore ()
{
m_timer.cancel ();
#if LOG_GC
bassert (!m_used.isSignaled ());
#endif
dispose (m_pool1);
dispose (m_pool2);
#if LOG_GC
bassert (!m_total.isSignaled ());
#endif
}
//------------------------------------------------------------------------------
void* PagedFreeStore::allocate ()
{
Page* page = m_hot->fresh->pop_front ();
if (!page)
{
#if HARD_LIMIT
const bool exhausted = m_newPagesLeft.release ();
if (exhausted)
fatal_error ("the limit of memory allocations was reached");
#endif
void* storage = ::malloc (m_pageBytes);
if (!storage)
fatal_error ("a memory allocation failed");
page = new (storage) Page (this);
#if LOG_GC
m_total.addref ();
#endif
}
#if LOG_GC
m_used.addref ();
#endif
return fromPage (page);
}
void PagedFreeStore::deallocate (void* const p)
{
Page* const page = toPage (p);
PagedFreeStore& allocator = page->getAllocator ();
allocator.m_hot->garbage->push_front (page);
#if LOG_GC
allocator.m_used.release ();
#endif
}
//
// Perform garbage collection.
//
void PagedFreeStore::onDeadlineTimer (DeadlineTimer&)
{
// Physically free one page.
// This will reduce the working set over time after a spike.
{
Page* page = m_cold->garbage->pop_front ();
if (page)
{
page->~Page ();
::free (page);
m_newPagesLeft.addref ();
#ifdef LOG_GC
m_total.release ();
#endif
}
}
std::swap (m_cold->fresh, m_cold->garbage);
// Swap atomically with respect to m_hot
Pool* temp = m_hot;
m_hot = m_cold; // atomic
m_cold = temp;
#if LOG_GC
String s;
s << "swap " << String (++m_swaps);
s << " (" << String (m_used.get ()) << "/"
<< String (m_total.get ()) << " of "
<< String (m_newPagesLeft.get ()) << ")";
Logger::outputDebugString (s);
#endif
m_timer.setExpiration (1.0);
}
void PagedFreeStore::dispose (Pages& pages)
{
for (;;)
{
Page* const page = pages.pop_front ();
if (page)
{
page->~Page ();
::free (page);
#if LOG_GC
m_total.release ();
#endif
}
else
{
break;
}
}
}
void PagedFreeStore::dispose (Pool& pool)
{
dispose (pool.fresh_c);
dispose (pool.garbage_c);
}

View File

@@ -1,102 +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_PAGEDFREESTORE_H_INCLUDED
#define BEAST_PAGEDFREESTORE_H_INCLUDED
/*============================================================================*/
/**
Lock-free memory allocator for fixed size pages.
The ABA problem (http://en.wikipedia.org/wiki/ABA_problem) is avoided by
treating freed pages as garbage, and performing a collection every second.
@ingroup beast_concurrent
*/
class BEAST_API PagedFreeStore : private DeadlineTimer::Listener
{
public:
explicit PagedFreeStore (const size_t pageBytes);
~PagedFreeStore ();
// The available bytes per page is a little bit less
// than requested in the constructor, due to overhead.
//
inline size_t getPageBytes () const
{
return m_pageBytesAvailable;
}
inline void* allocate (const size_t bytes)
{
if (bytes > m_pageBytes)
fatal_error ("the size is too large");
return allocate ();
}
void* allocate ();
static void deallocate (void* const p);
private:
void* newPage ();
void onDeadlineTimer (DeadlineTimer&);
private:
struct Page;
typedef LockFreeStack <Page> Pages;
struct Pool
{
Pool()
: fresh (&fresh_c.get())
, garbage (&garbage_c.get())
{
}
Pages* fresh;
Pages* garbage;
CacheLine::Padded <Pages> fresh_c;
CacheLine::Padded <Pages> garbage_c;
};
static inline void* fromPage (Page* const p);
static inline Page* toPage (void* const p);
void dispose (Pages& pages);
void dispose (Pool& pool);
private:
DeadlineTimer m_timer;
const size_t m_pageBytes;
const size_t m_pageBytesAvailable;
CacheLine::Aligned <Pool> m_pool1; // pair of pools
CacheLine::Aligned <Pool> m_pool2;
Pool* volatile m_cold; // pool which is cooling down
Pool* volatile m_hot; // pool we are currently using
AtomicCounter m_newPagesLeft; // limit of system allocations
#if 1
int m_swaps;
AtomicCounter m_total;
AtomicCounter m_used;
#endif
};
#endif

View File

@@ -20,18 +20,6 @@
#ifndef BEAST_STATICOBJECT_H_INCLUDED
#define BEAST_STATICOBJECT_H_INCLUDED
//
// A full suite of thread-safe objects designed for static storage duration.
//
// Wraps an object with a thread-safe initialization preamble so that it can
// properly exist with static storage duration.
//
// Implementation notes:
//
// This is accomplished by omitting the constructor and relying on the C++
// specification that plain data types with static storage duration are filled
// with zeroes before any other initialization code executes.
//
// Spec: N2914=09-0104
//
// [3.6.2] Initialization of non-local objects
@@ -40,84 +28,8 @@
// duration (3.7.2) shall be zero-initialized (8.5) before any
// other initialization takes place.
//
// Requirements:
//
// Object must be constructible without parameters.
// The StaticObject must be declared with static storage duration or
// the behavior is undefined.
//
// Usage example:
//
// Object* getInstance ()
// {
// static StaticObject <Object> instance;
// return instance->getObject ();
// }
//
namespace Static
{
//------------------------------------------------------------------------------
// Holds an object with static storage duration.
// The owner determines if and when the object is constructed and destroyed.
// Caller is responsible for synchronization.
//
template <class ObjectType, class Tag>
class Storage
{
public:
static inline void construct ()
{
new (getObjectPtr ()) ObjectType;
}
static inline void destroy ()
{
getObjectPtr ()->~ObjectType ();
}
static inline ObjectType* getObjectPtr ()
{
return reinterpret_cast <ObjectType*> (s_storage);
}
static inline ObjectType& getObject ()
{
return *getObjectPtr ();
}
inline ObjectType* operator-> () const
{
return getObjectPtr ();
}
inline ObjectType& operator* () const
{
return getObject ();
}
inline operator ObjectType* () const
{
return getObjectPtr ();
}
// TODO: Crashes on iOS if not accessed before usage
static char s_storage [sizeof (ObjectType)];
private:
};
template <class ObjectType, class Tag>
char Storage <ObjectType, Tag>::s_storage [sizeof (ObjectType)];
}
//------------------------------------------------------------------------------
namespace detail
{
namespace detail {
extern void staticObjectWait (std::size_t n);

View File

@@ -1,305 +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.
*/
//==============================================================================
CallQueue::CallQueue (String name)
: m_name (name)
{
}
CallQueue::~CallQueue ()
{
// Someone forget to close the queue.
bassert (m_closed.isSignaled ());
// Can't destroy queue with unprocessed calls.
bassert (m_queue.empty ());
}
bool CallQueue::isAssociatedWithCurrentThread () const
{
return Thread::getCurrentThreadId () == m_id;
}
// Adds a call to the queue of execution.
void CallQueue::queuep (Work* c)
{
// If this goes off it means calls are being made after the
// queue is closed, and probably there is no one around to
// process it.
bassert (!m_closed.isSignaled ());
if (m_queue.push_back (c))
signal ();
}
// Append the Work to the queue. If this call is made from the same
// thread as the last thread that called synchronize(), then the call
// will execute synchronously.
//
void CallQueue::callp (Work* c)
{
queuep (c);
// If we are called on the process thread and we are not
// recursed into doSynchronize, then process the queue. This
// makes calls from the process thread synchronous.
//
// NOTE: The value of isBeingSynchronized is invalid/volatile unless
// this thread is the last process thread.
//
// NOTE: There is a small window of opportunity where we
// might get an undesired synchronization if new thread
// calls synchronize() concurrently.
//
if (isAssociatedWithCurrentThread () &&
m_isBeingSynchronized.trySignal ())
{
doSynchronize ();
m_isBeingSynchronized.reset ();
}
}
bool CallQueue::synchronize ()
{
bool did_something;
// Detect recursion into doSynchronize(), and
// break ties for concurrent calls atomically.
//
if (m_isBeingSynchronized.trySignal ())
{
// Remember this thread.
m_id = Thread::getCurrentThreadId ();
did_something = doSynchronize ();
m_isBeingSynchronized.reset ();
}
else
{
did_something = false;
}
return did_something;
}
// Can still have pending calls, just can't put new ones in.
void CallQueue::close ()
{
m_closed.signal ();
synchronize ();
}
// Process everything in the queue. The list of pending calls is
// acquired atomically. New calls may enter the queue while we are
// processing.
//
// Returns true if any functors were called.
//
bool CallQueue::doSynchronize ()
{
bool did_something;
// Reset since we are emptying the queue. Since we loop
// until the queue is empty, it is possible for us to exit
// this function with an empty queue and signaled state.
//
reset ();
Work* call = m_queue.pop_front ();
if (call)
{
did_something = true;
// This method of processing one at a time has the desired
// side effect of synchronizing nested calls to us from a functor.
//
for (;;)
{
call->operator () ();
delete call;
call = m_queue.pop_front ();
if (call == 0)
break;
}
}
else
{
did_something = false;
}
return did_something;
}
//------------------------------------------------------------------------------
class CallQueueTests : public UnitTest
{
public:
enum
{
callsPerThread = 50000
};
struct TestQueue : CallQueue
{
explicit TestQueue (Thread& thread)
: CallQueue (thread.getThreadName())
, m_thread (thread)
{
}
void synchronize ()
{
CallQueue::synchronize();
}
void signal ()
{
m_thread.notify ();
}
void reset ()
{
}
void close ()
{
CallQueue::close();
}
Thread& m_thread;
};
struct TestThread : Thread
{
explicit TestThread (int id)
: Thread ("#" + String::fromNumber (id))
, m_queue (*this)
{
startThread ();
}
void stop ()
{
m_queue.call (&TestThread::doStop, this);
m_queue.close ();
}
void doStop ()
{
signalThreadShouldExit ();
notify ();
}
void func1 ()
{
m_string = m_string + String::fromNumber (m_random.nextInt(10));
if (m_string.length() > 100)
m_string = String::empty;
}
void func2 ()
{
m_string = m_string + String::fromNumber (m_random.nextInt());
if (m_string.length() > 100)
m_string = String::empty;
}
void func3 ()
{
m_string = m_string + m_string;
if (m_string.length() > 100)
m_string = m_string.substring (m_random.nextInt (30));
}
void run ()
{
while (! threadShouldExit ())
{
wait ();
m_queue.synchronize();
}
}
Random m_random;
TestQueue m_queue;
String m_string;
};
void testThreads (std::size_t nThreads)
{
beginTestCase (String::fromNumber (nThreads) + " threads");
OwnedArray <TestThread> threads;
threads.ensureStorageAllocated (nThreads);
for (std::size_t i = 0; i < nThreads; ++i)
threads.add (new TestThread (i + 1));
Time const startTime (Time::getCurrentTime());
for (std::size_t i = 0; i < callsPerThread * nThreads; ++i)
{
int const n (random().nextInt (threads.size()));
int const f (random().nextInt (3));
switch (f)
{
default:
bassertfalse;
case 0: threads[n]->m_queue.call (&TestThread::func1, threads[n]); break;
case 1: threads[n]->m_queue.call (&TestThread::func2, threads[n]); break;
case 2: threads[n]->m_queue.call (&TestThread::func3, threads[n]); break;
};
}
for (int i = 0; i < threads.size(); ++i)
threads[i]->stop ();
for (int i = 0; i < threads.size(); ++i)
threads[i]->waitForThreadToExit();
double const secondsElapsed ((Time::getCurrentTime() - startTime).inSeconds ());
pass ();
std::size_t const totalCalls (callsPerThread * nThreads);
double const callsPerSecond = (totalCalls / secondsElapsed);
logMessage (String::fromNumber (callsPerSecond + 0.5, 0) + " calls/second (in " +
String::fromNumber (secondsElapsed, 1) + " seconds)");
}
void runTest ()
{
testThreads (8);
testThreads (64);
}
CallQueueTests () : UnitTest ("CallQueue", "beast", runManual)
{
}
};
static CallQueueTests callQueueTests;

View File

@@ -1,506 +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_CALLQUEUE_H_INCLUDED
#define BEAST_CALLQUEUE_H_INCLUDED
/*============================================================================*/
/**
A FIFO for calling functors asynchronously.
This object is an alternative to traditional locking techniques used to
implement concurrent systems. Instead of acquiring a mutex to change shared
data, a functor is queued for later execution (usually on another thread). The
execution of the functor applies the transformation to the shared state that
was formerly performed within a lock (i.e. CriticalSection).
For read operations on shared data, instead of acquiring a mutex and
accessing the data directly, copies are made (one for each thread), and the
thread accesses its copy without acquiring a lock. One thread owns the master
copy of the shared state. Requests for changing shared state are made by other
threads by posting functors to the master thread's CallQueue. The master
thread notifies other threads of changes by posting functors to their
respective associated CallQueue, using the Listeners interface.
The purpose of the functor is to encapsulate one mutation of shared state to
guarantee progress towards a consensus of the concurrent data among
participating threads. Functors should execute quickly, ideally in constant
time. Dynamically allocated objects of class type passed as functor parameters
should, in general, be reference counted. The ConcurrentObject class is ideal
for meeting this requirement, and has the additional benefit that the workload
of deletion is performed on a separate, provided thread. This queue is not a
replacement for a thread pool or job queue type system.
A CallQueue is considered signaled when one or more functors are present.
Functors are executed during a call to synchronize(). The operation of
executing functors via the call to synchronize() is called synchronizing
the queue. It can more generally be thought of as synchronizing multiple
copies of shared data between threads.
Although there is some extra work required to set up and maintain this
system, the benefits are significant. Since shared data is only synchronized
at well defined times, the programmer can reason and make strong statements
about the correctness of the concurrent system. For example, if an
AudioIODeviceCallback synchronizes the CallQueue only at the beginning of its
execution, it is guaranteed that shared data will remain the same throughout
the remainder of the function.
Because shared data is accessed for reading without a lock, upper bounds
on the run time performance can easily be calculated and assured. Compare
this with the use of a mutex - the run time performance experiences a
combinatorial explosion of possibilities depending on the complex interaction
of multiple threads.
Since a CallQueue is almost always used to invoke parameterized member
functions of objects, the call() function comes in a variety of convenient
forms to make usage easy:
@code
void func1 (int);
struct Object
{
void func2 (void);
void func3 (String name);
static void func4 ();
};
CallQueue fifo ("Example");
void example ()
{
fifo.call (func1, 42); // same as: func1 (42)
Object* object = new Object;
fifo.call (&Object::func2, object); // same as: object->func2 ()
fifo.call (&Object::func3, // same as: object->funcf ("Label")
object,
"Label");
fifo.call (&Object::func4); // even static members can be called.
fifo.callf (functional::bind (&Object::func2, // same as: object->func2 ()
object));
}
@endcode
@invariant Functors can be added from any thread at any time, to any queue
which is not closed.
@invariant When synchronize() is called, functors are called and deleted.
@invariant The thread from which synchronize() is called is considered the
thread associated with the CallQueue.
@invariant Functors queued by the same thread always execute in the same
order they were queued.
@invariant Functors are guaranteed to execute. It is an error if the
CallQueue is deleted while there are functors in it.
Normally, you will not use CallQueue directly, but one of its subclasses
instead. The CallQueue is one of a handful of objects that work together to
implement this system of concurrent data access.
For performance considerations, this implementation is wait-free for
producers and mostly wait-free for consumers. It also uses a lock-free
and wait-free (in the fast path) custom memory allocator.
@see GuiCallQueue, ManualCallQueue, MessageThread, ThreadWithCallQueue
@ingroup beast_concurrent
*/
class BEAST_API CallQueue
{
public:
//============================================================================
/** Type of allocator to use.
@internal
*/
typedef FifoFreeStoreType AllocatorType;
/** Abstract nullary functor in a @ref CallQueue.
Custom implementations may derive from this object for efficiency instead
of using the automatic binding functions.
*/
class Work : public LockFreeQueue <Work>::Node,
public AllocatedBy <AllocatorType>
{
public:
virtual ~Work () { }
/** Calls the functor.
This executes during the queue's call to synchronize().
*/
virtual void operator () () = 0;
};
//============================================================================
/** Create the CallQueue.
The queue starts out open and empty.
@param name A string to identify the queue during debugging.
*/
explicit CallQueue (String name);
/** Destroy the CallQueue.
@invariant Destroying a queue that contains functors results in undefined
behavior.
@note It is customary to call close() on the CallQueue early in the
shutdown process to catch functors going into the queue late.
*/
virtual ~CallQueue ();
//============================================================================
/** Add a functor and possibly synchronize.
Use this when you want to perform the bind yourself.
@param f The functor to add, typically the return value of a call
to bind().
@see call
*/
template <class Functor>
void callf (Functor f)
{
callp (new (m_allocator) CallType <Functor> (f));
}
/** Add a function call and possibly synchronize.
Parameters are evaluated immediately and added to the queue as a packaged
functor. If the current thread of execution is the same as the thread
associated with the CallQueue, synchronize() is called automatically. This
behavior can be avoided by using queue() instead.
@param f The function to call followed by up to eight parameters,
evaluated immediately. The parameter list must match the function
signature. For class member functions, the first argument must be a
pointer to the class object.
@see queue
@todo Provide an example of when synchronize() is needed in call().
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Fn>
void call (Fn f)
{ callf (functional::bind (f)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Fn, class T1>
void call (Fn f, T1 t1)
{ callf (functional::bind (f, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Fn, class T1, class T2>
void call (Fn f, T1 t1, T2 t2)
{ callf (functional::bind (f, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Fn, class T1, class T2, class T3>
void call (Fn f, T1 t1, T2 t2, T3 t3)
{ callf (functional::bind (f, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Fn, class T1, class T2, class T3, class T4>
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
{ callf (functional::bind (f, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Fn, class T1, class T2, class T3, class T4, class T5>
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ callf (functional::bind (f, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
/** Add a functor without synchronizing.
Use this when you want to perform the bind yourself.
@param f The functor to add, typically the return value of a call
to bind().
@see queue
*/
template <class Functor>
void queuef (Functor f)
{
queuep (new (m_allocator) CallType <Functor> (f));
}
/** Add a function call without synchronizing.
Parameters are evaluated immediately, then the resulting functor is added
to the queue. This is used to postpone the call to synchronize() when
there would be adverse side effects to executing the function immediately.
In this example, we use queue() instead of call() to avoid a deadlock:
@code
struct SharedState; // contains data shared between threads
SharedData <SharedState> sharedState;
void stateChanged ()
{
SharedData <SharedState>::ConstAccess state (sharedState);
// (read state)
}
CallQueue fifo;
void changeState ()
{
SharedData <State>::Access state (sharedState);
// (read and write state)
fifo.call (&stateChanged); // BUG: DEADLOCK because of the implicit synchronize().
fifo.queue (&stateChanged); // Okay, synchronize() will be called later,
// after the write lock is released.
}
@endcode
@param f The function to call followed by up to eight parameters,
evaluated immediately. The parameter list must match the
function signature. For non-static class member functions,
the first argument must be a pointer an instance of the class.
@see call
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Fn>
void queue (Fn f)
{ queuef (functional::bind (f)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Fn, class T1>
void queue (Fn f, T1 t1)
{ queuef (functional::bind (f, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Fn, class T1, class T2>
void queue (Fn f, T1 t1, T2 t2)
{ queuef (functional::bind (f, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Fn, class T1, class T2, class T3>
void queue (Fn f, T1 t1, T2 t2, T3 t3)
{ queuef (functional::bind (f, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Fn, class T1, class T2, class T3, class T4>
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
{ queuef (functional::bind (f, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Fn, class T1, class T2, class T3, class T4, class T5>
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ queuef (functional::bind (f, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
protected:
//============================================================================
/** Synchronize the queue.
A synchronize operation calls all functors in the queue. If a functor
causes additional functors to be added, they are eventually executed
before synchronize() returns. Derived class call this when the queue is
signaled, and optionally at any other time. Calling this function from
more than one thread simultaneously is undefined.
@return true if any functors were executed.
*/
bool synchronize ();
/** Close the queue.
Functors may not be added after this routine is called. This is used for
diagnostics, to track down spurious calls during application shutdown
or exit. Derived classes may call this if the appropriate time is known.
The queue is synchronized after it is closed.
*/
void close ();
/** Called when the queue becomes signaled.
A queue is signaled on the transition from empty to non-empty. Derived
classes implement this function to perform a notification so that
synchronize() will be called. For example, by triggering a WaitableEvent.
@note Due to the implementation the queue can remain signaled for one
extra cycle. This does not happen under load and is not an issue
in practice.
*/
virtual void signal () = 0;
/** Called when the queue is reset.
A queue is reset when it was previously signaled and then becomes empty
as a result of a call to synchronize.
*/
virtual void reset () = 0;
public:
//============================================================================
/** Add a raw call.
@internal
Custom implementations use this to control the allocation.
@param c The call to add. The memory must come from the allocator.
*/
void callp (Work* c);
/** Queue a raw call.
Custom implementations use this to control the allocation.
@param c The call to add. The memory must come from the allocator.
*/
void queuep (Work* c);
/** Retrieve the allocator.
@return The allocator to use when allocating a raw Work object.
*/
inline AllocatorType& getAllocator ()
{
return m_allocator;
}
/** See if the caller is on the association thread.
@return `true` if the calling thread of execution is associated with the
queue.
*/
bool isAssociatedWithCurrentThread () const;
/** See if the queue is being synchronized.
This is used for diagnostics.
@note This must be called from the associated thread or else the return
value is undefined.
@return `true` if the call stack contains synchronize() for this queue.
*/
bool isBeingSynchronized () const
{
return m_isBeingSynchronized.isSignaled ();
}
private:
template <class Functor>
class CallType : public Work
{
public:
explicit CallType (Functor f) : m_f (f) { }
void operator () ()
{
m_f ();
}
private:
Functor m_f;
};
bool doSynchronize ();
private:
String const m_name;
Thread::ThreadID m_id;
LockFreeQueue <Work> m_queue;
AtomicFlag m_closed;
AtomicFlag m_isBeingSynchronized;
AllocatorType m_allocator;
};
#endif

View File

@@ -30,8 +30,9 @@ public:
The listener is called on an auxiliary thread. It is suggested
not to perform any time consuming operations during the call.
*/
// VFALCO TODO Allow construction with a specific ThreadWithCallQueue&
// on which to notify the listener.
// VFALCO TODO Perhaps allow construction using a ServiceQueue to use
// for notifications.
//
class Listener
{
public:

View File

@@ -1,275 +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.
*/
//==============================================================================
InterruptibleThread::ThreadHelper::ThreadHelper (String name,
InterruptibleThread* owner)
: Thread (name)
, m_owner (owner)
{
}
InterruptibleThread* InterruptibleThread::ThreadHelper::getOwner () const
{
return m_owner;
}
void InterruptibleThread::ThreadHelper::run ()
{
m_owner->run ();
}
//------------------------------------------------------------------------------
InterruptibleThread::InterruptibleThread (String name)
: m_thread (name, this)
, m_entryPoint (nullptr)
, m_state (stateRun)
{
}
InterruptibleThread::~InterruptibleThread ()
{
m_runEvent.signal ();
join ();
}
void InterruptibleThread::start (EntryPoint* const entryPoint)
{
m_entryPoint = entryPoint;
m_thread.startThread ();
// Prevent data race with member variables
//
m_runEvent.signal ();
}
void InterruptibleThread::join ()
{
m_thread.signalThreadShouldExit();
m_thread.notify();
interrupt();
m_thread.stopThread (-1);
}
// Block until there is an interruption.
// This counts as an interruption point.
//
void InterruptibleThread::wait ()
{
// Can only be called from the thread of execution.
bassert (isTheCurrentThread ());
for (;;)
{
// Impossible for us to already be in the wait state.
bassert (m_state != stateWait);
// See if we are interrupted.
if (m_state.tryChangeState (stateInterrupt, stateRun))
{
// We were interrupted, so the wait is satisfied.
return;
}
else
{
// Try to get into the wait state.
if (m_state.tryChangeState (stateRun, stateWait))
{
bassert (m_state == stateWait);
// Got into the wait state so block until interrupt.
m_thread.wait ();
// Event is signalled means we were
// interrupted, so the wait is satisfied.
bassert (m_state != stateWait || m_thread.threadShouldExit ());
return;
}
}
}
}
void InterruptibleThread::interrupt ()
{
for (;;)
{
int const state (m_state);
if (state == stateInterrupt ||
m_state.tryChangeState (stateRun, stateInterrupt))
{
// We got into the interrupt state, the thead
// will see this at the next interruption point.
//
// Thread will see this at next interruption point.
//
break;
}
else if (m_state.tryChangeState (stateWait, stateRun))
{
m_thread.notify ();
break;
}
}
}
// Returns true if the thead function should stop
// its activities as soon as possible and return.
//
bool InterruptibleThread::interruptionPoint ()
{
// Can only be called from the thread of execution.
bassert (isTheCurrentThread ());
// Impossible for this to be called in the wait state.
bassert (m_state != stateWait);
bool const interrupted (m_state.tryChangeState (stateInterrupt, stateRun));
return interrupted;
}
InterruptibleThread::id InterruptibleThread::getId () const
{
return m_threadId;
}
bool InterruptibleThread::isTheCurrentThread () const
{
return m_thread.getCurrentThreadId () == m_threadId;
}
void InterruptibleThread::setPriority (int priority)
{
m_thread.setPriority (priority);
}
InterruptibleThread* InterruptibleThread::getCurrentThread ()
{
InterruptibleThread* result = nullptr;
Thread* const thread = Thread::getCurrentThread ();
if (thread != nullptr)
{
ThreadHelper* const helper = dynamic_cast <ThreadHelper*> (thread);
bassert (helper != nullptr);
result = helper->getOwner ();
}
return result;
}
void InterruptibleThread::run ()
{
m_threadId = m_thread.getThreadId ();
m_runEvent.wait ();
m_entryPoint->threadRun ();
}
//------------------------------------------------------------------------------
bool CurrentInterruptibleThread::interruptionPoint ()
{
bool interrupted = false;
InterruptibleThread* const interruptibleThread (InterruptibleThread::getCurrentThread ());
bassert (interruptibleThread != nullptr);
interrupted = interruptibleThread->interruptionPoint ();
return interrupted;
}
//------------------------------------------------------------------------------
class InterruptibleThreadTests : public UnitTest
{
public:
enum
{
callsPerThread = 100000
};
struct TestThread : InterruptibleThread::EntryPoint
{
explicit TestThread (int id)
: m_thread ("#" + String::fromNumber (id))
{
m_thread.start (this);
}
void threadRun ()
{
while (! m_thread.peekThread().threadShouldExit())
{
String s;
while (!m_thread.interruptionPoint ())
{
s = s + String::fromNumber (m_random.nextInt ());
if (s.length () > 100)
s = String::empty;
}
}
}
Random m_random;
InterruptibleThread m_thread;
};
void testThreads (std::size_t nThreads)
{
beginTestCase (String::fromNumber (nThreads) + " threads");
OwnedArray <TestThread> threads;
threads.ensureStorageAllocated (nThreads);
for (std::size_t i = 0; i < nThreads; ++i)
threads.add (new TestThread (i + 1));
for (std::size_t i = 0; i < callsPerThread * nThreads; ++i)
{
int const n (random().nextInt (threads.size()));
threads[n]->m_thread.interrupt();
}
pass ();
}
void runTest ()
{
testThreads (8);
testThreads (64);
}
InterruptibleThreadTests () : UnitTest ("InterruptibleThread", "beast")
{
}
};
static InterruptibleThreadTests interruptibleThreadTests;

View File

@@ -1,180 +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_INTERRUPTIBLETHREAD_H_INCLUDED
#define BEAST_INTERRUPTIBLETHREAD_H_INCLUDED
//==============================================================================
/**
A thread with soft interruption support.
The thread must periodically call interruptionPoint(), which returns `true`
the first time an interruption has occurred since the last call to
interruptionPoint().
To create a thread, derive your class from InterruptibleThread::EntryPoint
and implement the threadRun() function. Then, call run() with your object.
@ingroup beast_core
*/
class BEAST_API InterruptibleThread
{
public:
/** InterruptibleThread entry point.
*/
class EntryPoint
{
public:
virtual ~EntryPoint () { }
virtual void threadRun () = 0;
};
public:
typedef Thread::ThreadID id;
/** Construct an interruptible thread.
The name is used for debugger diagnostics.
@param name The name of the thread.
*/
explicit InterruptibleThread (String name);
/** Destroy the interruptible thread.
This will signal an interrupt and wait until the thread exits.
*/
~InterruptibleThread ();
/** Start the thread.
*/
void start (EntryPoint* const entryPoint);
/** Wait for the thread to exit.
*/
void join ();
/** Wait for interrupt.
This call blocks until the thread is interrupted.
May only be called by the thread of execution.
*/
void wait ();
/** Interrupt the thread of execution.
This can be called from any thread.
*/
void interrupt ();
/** Determine if an interruption is requested.
After the function returns `true`, the interrupt status is cleared.
Subsequent calls will return `false` until another interrupt is requested.
May only be called by the thread of execution.
@see CurrentInterruptibleThread::interruptionPoint
@return `true` if an interrupt was requested.
*/
bool interruptionPoint ();
/** Get the ID of the associated thread.
@return The ID of the thread.
*/
id getId () const;
/** Determine if this is the thread of execution.
@note The return value is undefined if the thread is not running.
@return `true` if the caller is this thread of execution.
*/
bool isTheCurrentThread () const;
/** Adjust the thread priority.
@note This only affects some platforms.
@param priority A number from 0..10
*/
void setPriority (int priority);
/** Get the InterruptibleThread for the thread of execution.
This will return `nullptr` when called from the message thread, or from
a thread of execution that is not an InterruptibleThread.
*/
static InterruptibleThread* getCurrentThread ();
// private
Thread& peekThread ()
{
return m_thread;
}
private:
class ThreadHelper : public Thread
{
public:
ThreadHelper (String name, InterruptibleThread* owner);
InterruptibleThread* getOwner () const;
void run ();
private:
InterruptibleThread* const m_owner;
};
void run ();
ThreadHelper m_thread;
EntryPoint* m_entryPoint;
WaitableEvent m_runEvent;
id m_threadId;
enum
{
stateRun,
stateInterrupt,
stateWait
};
AtomicState m_state;
};
//------------------------------------------------------------------------------
/** Global operations on the current InterruptibleThread.
Calling members of the class from a thread of execution which is not an
InterruptibleThread results in undefined behavior.
*/
class CurrentInterruptibleThread
{
public:
/** Call the current thread's interrupt point function.
*/
static bool interruptionPoint ();
};
#endif

View File

@@ -1,758 +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.
*/
//==============================================================================
// CallQueue item to process a Call for a particular listener.
// This is used to avoid bind overhead.
//
class ListenersBase::CallWork : public CallQueue::Work
{
public:
inline CallWork (ListenersBase::Call* const c, void* const listener)
: m_call (c), m_listener (listener)
{
}
void operator () ()
{
m_call->operator () (m_listener);
}
private:
ListenersBase::Call::Ptr m_call;
void* const m_listener;
};
//------------------------------------------------------------------------------
// CallQueue item to process a Call for a group.
// This is used to avoid bind overhead.
//
class ListenersBase::GroupWork : public CallQueue::Work
{
public:
inline GroupWork (Group* group,
ListenersBase::Call* c,
const timestamp_t timestamp)
: m_group (group)
, m_call (c)
, m_timestamp (timestamp)
{
}
void operator () ()
{
m_group->do_call (m_call, m_timestamp);
}
private:
Group::Ptr m_group;
ListenersBase::Call::Ptr m_call;
const timestamp_t m_timestamp;
};
//------------------------------------------------------------------------------
// CallQueue item to process a call for a particular listener.
// This is used to avoid bind overhead.
//
class ListenersBase::GroupWork1 : public CallQueue::Work
{
public:
inline GroupWork1 (Group* group,
ListenersBase::Call* c,
const timestamp_t timestamp,
void* const listener)
: m_group (group)
, m_call (c)
, m_timestamp (timestamp)
, m_listener (listener)
{
}
void operator () ()
{
m_group->do_call1 (m_call, m_timestamp, m_listener);
}
private:
Group::Ptr m_group;
ListenersBase::Call::Ptr m_call;
const timestamp_t m_timestamp;
void* const m_listener;
};
//------------------------------------------------------------------------------
// A Proxy maintains a list of Entry.
// Each Entry holds a group and the current Call (which can be updated).
//
struct ListenersBase::Proxy::Entry : Entries::Node,
SharedObject,
AllocatedBy <AllocatorType>
{
typedef SharedPtr <Entry> Ptr;
explicit Entry (Group* g)
: group (g)
{
}
~Entry ()
{
bassert (call.get () == 0);
}
Group::Ptr group;
AtomicPointer <Call> call;
};
//------------------------------------------------------------------------------
// A Group maintains a list of Entry.
//
struct ListenersBase::Group::Entry : List <Entry>::Node,
AllocatedBy <AllocatorType>
{
Entry (void* const l, const timestamp_t t)
: listener (l)
, timestamp (t)
{
}
void* const listener;
const timestamp_t timestamp;
};
//------------------------------------------------------------------------------
//
// Group
//
//------------------------------------------------------------------------------
// - A list of listeners associated with the same CallQueue.
//
// - The list is only iterated on the CallQueue's thread.
//
// - It is safe to add or remove listeners from the group
// at any time.
//
ListenersBase::Group::Group (CallQueue& callQueue)
: m_fifo (callQueue)
, m_listener (0)
{
}
ListenersBase::Group::~Group ()
{
// If this goes off it means a Listener forgot to remove itself.
bassert (m_list.empty ());
// shouldn't be deleting group during a call
bassert (m_listener == 0);
}
// Add the listener with the given timestamp.
// The listener will only get calls with higher timestamps.
// The caller must prevent duplicates.
//
void ListenersBase::Group::add (void* listener,
const timestamp_t timestamp,
AllocatorType& allocator)
{
ReadWriteMutex::ScopedWriteLockType lock (m_mutex);
bassert (!contains (listener));
// Should never be able to get here while in call()
bassert (m_listener == 0);
// Add the listener and remember the time stamp so we don't
// send it calls that were queued earlier than the add().
m_list.push_back (*new (allocator) Entry (listener, timestamp));
}
// Removes the listener from the group if it exists.
// Returns true if the listener was removed.
//
bool ListenersBase::Group::remove (void* listener)
{
bool found = false;
ReadWriteMutex::ScopedWriteLockType lock (m_mutex);
// Should never be able to get here while in call()
bassert (m_listener == 0);
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end (); ++iter)
{
Entry* entry = & (*iter);
if (entry->listener == listener)
{
m_list.erase (m_list.iterator_to (*entry));
delete entry;
found = true;
break;
}
}
return found;
}
// Used for assertions.
// The caller must synchronize.
//
bool ListenersBase::Group::contains (void* const listener) /*const*/
{
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end (); iter++)
if (iter->listener == listener)
return true;
return false;
}
void ListenersBase::Group::call (Call* const c, const timestamp_t timestamp)
{
bassert (!empty ());
m_fifo.callp (new (m_fifo.getAllocator ()) GroupWork (this, c, timestamp));
}
void ListenersBase::Group::queue (Call* const c, const timestamp_t timestamp)
{
bassert (!empty ());
m_fifo.queuep (new (m_fifo.getAllocator ()) GroupWork (this, c, timestamp));
}
void ListenersBase::Group::call1 (Call* const c,
const timestamp_t timestamp,
void* const listener)
{
m_fifo.callp (new (m_fifo.getAllocator ()) GroupWork1 (
this, c, timestamp, listener));
}
void ListenersBase::Group::queue1 (Call* const c,
const timestamp_t timestamp,
void* const listener)
{
m_fifo.queuep (new (m_fifo.getAllocator ()) GroupWork1 (
this, c, timestamp, listener));
}
// Queues a reference to the Call on the thread queue of each listener
// that is currently in our list. The thread queue must be in the
// stack's call chain, either directly from CallQueue::synchronize(),
// or from Proxy::do_call() called from CallQueue::synchronize().
//
void ListenersBase::Group::do_call (Call* const c, const timestamp_t timestamp)
{
if (!empty ())
{
ReadWriteMutex::ScopedReadLockType lock (m_mutex);
// Recursion not allowed.
bassert (m_listener == 0);
// The body of the loop MUST NOT cause listeners to get called.
// Therefore, we don't have to worry about listeners removing
// themselves while iterating the list.
//
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end ();)
{
Entry* entry = & (*iter++);
// Since it is possible for a listener to be added after a
// Call gets queued but before it executes, this prevents listeners
// from seeing Calls created before they were added.
//
if (timestamp > entry->timestamp)
{
m_listener = entry->listener;
// The thread queue's synchronize() function MUST be in our call
// stack to guarantee that these calls will not execute immediately.
// They will be handled by the tail recusion unrolling in the
// thread queue.
bassert (m_fifo.isBeingSynchronized ());
m_fifo.callp (new (m_fifo.getAllocator ()) CallWork (c, m_listener));
m_listener = 0;
}
}
}
else
{
// last listener was removed before we got here,
// and the parent listener list may have been deleted.
}
}
void ListenersBase::Group::do_call1 (Call* const c, const timestamp_t timestamp,
void* const listener)
{
if (!empty ())
{
ReadWriteMutex::ScopedReadLockType lock (m_mutex);
// Recursion not allowed.
bassert (m_listener == 0);
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end ();)
{
Entry* entry = & (*iter++);
if (entry->listener == listener)
{
if (timestamp > entry->timestamp)
{
m_listener = entry->listener;
bassert (m_fifo.isBeingSynchronized ());
m_fifo.callp (new (m_fifo.getAllocator ()) CallWork (c, m_listener));
m_listener = 0;
}
}
}
}
else
{
// Listener was removed
}
}
//------------------------------------------------------------------------------
//
// Proxy
//
//------------------------------------------------------------------------------
// CallQueue item for processing a an Entry for a Proxy.
// This is used to avoid bind overhead.
//
class ListenersBase::Proxy::Work : public CallQueue::Work
{
public:
inline Work (Entry* const entry, const timestamp_t timestamp)
: m_entry (entry)
, m_timestamp (timestamp)
{
}
void operator () ()
{
ListenersBase::Call* c = m_entry->call.exchange (0);
Group* group = m_entry->group;
if (!group->empty ())
group->do_call (c, m_timestamp);
c->decReferenceCount ();
}
private:
Entry::Ptr m_entry;
const timestamp_t m_timestamp;
};
// Holds a Call, and gets put in the CallQueue in place of the Call.
// The Call may be replaced if it hasn't been processed yet.
// A Proxy exists for the lifetime of the Listeners.
//
ListenersBase::Proxy::Proxy (void const* const member, const size_t bytes)
: m_bytes (bytes)
{
if (bytes > maxMemberBytes)
fatal_error ("the Proxy member is too large");
memcpy (m_member, member, bytes);
}
ListenersBase::Proxy::~Proxy ()
{
// If the proxy is getting destroyed it means:
// - the listeners object is getting destroyed
// - all listeners must have removed themselves
// - all thread queues have been fully processed
// Therefore, our entries should be gone.
// NO it is possible for an empty Group, for which
// the parent listeners object has been destroyed,
// to still exist in a thread queue!!!
// But all listeners should have removed themselves
// so our list of groups should still be empty.
bassert (m_entries.empty ());
}
// Adds the group to the Proxy.
// Caller must have the proxies mutex.
// Caller is responsible for preventing duplicates.
//
void ListenersBase::Proxy::add (Group* group, AllocatorType& allocator)
{
Entry* entry (new (allocator) Entry (group));
// Manual addref and put raw pointer in list
entry->incReferenceCount ();
m_entries.push_back (*entry);
}
// Removes the group from the Proxy.
// Caller must have the proxies mutex.
// Caller is responsible for making sure the group exists.
void ListenersBase::Proxy::remove (Group* group)
{
for (Entries::iterator iter = m_entries.begin (); iter != m_entries.end ();)
{
Entry* entry = & (*iter++);
if (entry->group == group)
{
// remove from list and manual release
m_entries.erase (m_entries.iterator_to (*entry));
entry->decReferenceCount ();
// Entry might still be in the empty group's thread queue
break;
}
}
}
// For each group, updates the call.
// Queues each group that isn't already queued.
// Caller must acquire the group read lock.
//
void ListenersBase::Proxy::update (Call* const c, const timestamp_t timestamp)
{
// why would we even want to be called?
bassert (!m_entries.empty ());
// With the read lock, this list can't change on us unless someone
// adds a listener to a new thread queue in response to a call.
for (Entries::iterator iter = m_entries.begin (); iter != m_entries.end ();)
{
Entry* entry = & (*iter++);
// Manually add a reference since we use a raw pointer
c->incReferenceCount ();
// Atomically exchange the new call for the old one
Call* old = entry->call.exchange (c);
// If no old call then they need to be queued
if (!old)
{
CallQueue& callQueue = entry->group->getCallQueue ();
callQueue.callp (new (callQueue.getAllocator ()) Work (entry, timestamp));
}
else
{
old->decReferenceCount ();
}
}
}
bool ListenersBase::Proxy::match (void const* const member, const size_t bytes) const
{
return m_bytes == bytes && memcmp (member, m_member, bytes) == 0;
}
//------------------------------------------------------------------------------
//
// ListenersBase
//
//------------------------------------------------------------------------------
ListenersBase::ListenersBase ()
: m_timestamp (0)
, m_allocator (AllocatorType::getInstance ())
, m_callAllocator (CallAllocatorType::getInstance ())
{
}
ListenersBase::~ListenersBase ()
{
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
// If this goes off it means a Listener forgot to remove.
bassert (group->empty ());
group->decReferenceCount ();
}
// Proxies are never deleted until here.
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
delete & (*iter++);
}
void ListenersBase::add_void (void* const listener, CallQueue& callQueue)
{
ReadWriteMutex::ScopedWriteLockType lock (m_groups_mutex);
#if BEAST_DEBUG
// Make sure the listener has not already been added
// SHOULD USE const_iterator!
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
// We can be in do_call() on another thread now, but it
// doesn't modify the list, and we have the write lock.
bassert (!group->contains (listener));
}
#endif
// See if we already have a Group for this thread queue.
Group::Ptr group;
// SHOULD USE const_iterator
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group::Ptr cur = & (*iter++);
if (&cur->getCallQueue () == &callQueue)
{
group = cur;
break;
}
}
if (!group)
{
group = new (m_allocator) Group (callQueue);
// Add it to the list, and give it a manual ref
// since the list currently uses raw pointers.
group->incReferenceCount ();
m_groups.push_back (*group);
// Tell existing proxies to add the group
ReadWriteMutex::ScopedReadLockType lock (m_proxies_mutex);
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
(iter++)->add (group, *m_allocator);
}
// Add the listener to the group with the current timestamp
group->add (listener, m_timestamp, *m_allocator);
// Increment the timestamp within the mutex so
// future calls will be newer than this listener.
++m_timestamp;
}
void ListenersBase::remove_void (void* const listener)
{
ReadWriteMutex::ScopedWriteLockType lock (m_groups_mutex);
// Make sure the listener exists
#if BEAST_DEBUG
{
bool exists = false;
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
// this should never happen while we hold the mutex
bassert (!group->empty ());
if (group->contains (listener))
{
bassert (!exists); // added twice?
exists = true;
// keep going to make sure there are no empty groups
}
}
bassert (exists);
}
#endif
// Find the group and remove
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group::Ptr group = & (*iter++);
// If the listener is in there, take it out.
if (group->remove (listener))
{
// Are we the last listener?
if (group->empty ())
{
// Tell proxies to remove the group
{
ReadWriteMutex::ScopedWriteLockType lock (m_proxies_mutex);
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
{
Proxy* proxy = & (*iter++);
proxy->remove (group);
}
}
// Remove it from the list and manually release
// the reference since the list uses raw pointers.
m_groups.erase (m_groups.iterator_to (*group));
group->decReferenceCount ();
// It is still possible for the group to exist at this
// point in a thread queue but it will get processed,
// do nothing, and release its own final reference.
}
break;
}
}
}
void ListenersBase::callp (Call::Ptr cp)
{
Call* c = cp;
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
// can't be const iterator because queue() might cause called functors
// to modify the list.
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
(iter++)->call (c, m_timestamp);
}
void ListenersBase::queuep (Call::Ptr cp)
{
Call* c = cp;
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
// can't be const iterator because queue() might cause called functors
// to modify the list.
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
(iter++)->queue (c, m_timestamp);
}
void ListenersBase::call1p_void (void* const listener, Call* c)
{
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
// can't be const iterator because queue() might cause called functors
// to modify the list.
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
if (group->contains (listener))
{
group->call1 (c, m_timestamp, listener);
break;
}
}
}
void ListenersBase::queue1p_void (void* const listener, Call* c)
{
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
// can't be const iterator because queue() might cause called functors
// to modify the list.
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
if (group->contains (listener))
{
group->queue1 (c, m_timestamp, listener);
break;
}
}
}
// Search for an existing Proxy that matches the pointer to
// member and replace it's Call, or create a new Proxy for it.
//
void ListenersBase::updatep (void const* const member,
const size_t bytes, Call::Ptr cp)
{
Call* c = cp;
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
if (!m_groups.empty ())
{
Proxy* proxy;
{
ReadWriteMutex::ScopedReadLockType lock (m_proxies_mutex);
// See if there's already a proxy
proxy = find_proxy (member, bytes);
}
// Possibly create one
if (!proxy)
{
ReadWriteMutex::ScopedWriteLockType lock (m_proxies_mutex);
// Have to search for it again in case someone else added it
proxy = find_proxy (member, bytes);
if (!proxy)
{
// Create a new empty proxy
proxy = new (m_allocator) Proxy (member, bytes);
// Add all current groups to the Proxy.
// We need the group read lock for this (caller provided).
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
{
Group* group = & (*iter++);
proxy->add (group, *m_allocator);
}
// Add it to the list.
m_proxies.push_front (*proxy);
}
}
// Requires the group read lock
proxy->update (c, m_timestamp);
}
}
// Searches for a proxy that matches the pointer to member.
// Caller synchronizes.
//
ListenersBase::Proxy* ListenersBase::find_proxy (const void* member, size_t bytes)
{
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
{
Proxy* proxy = & (*iter++);
if (proxy->match (member, bytes))
return proxy;
}
return 0;
}

View File

@@ -1,797 +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_LISTENERS_H_INCLUDED
#define BEAST_LISTENERS_H_INCLUDED
/*============================================================================*/
/**
A group of concurrent Listeners.
A Listener is an object of class type which inherits from a defined
interface, and registers on a provided instance of Listeners to receive
asynchronous notifications of changes to concurrent states. Another way of
defining Listeners, is that it is similar to a Juce ListenerList but with
the provision that the Listener registers with the CallQueue upon which the
notification should be made.
Listeners makes extensive use of CallQueue for providing the notifications,
and provides a higher level facility for implementing the concurrent
synchronization strategy outlined in CallQueue. Therefore, the same notes
which apply to functors in CallQueue also apply to Listener member
invocations. Their execution time should be brief, limited in scope to
updating the recipient's view of a shared state, and use reference counting
for parameters of class type.
To use this system, first declare your Listener interface:
@code
struct Listener
{
// Sent on every output block
virtual void onOutputLevelChanged (const float outputLevel) { }
};
@endcode
Now set up the place where you want to send the notifications. In this
example, we will set up the AudioIODeviceCallback to notify anyone who is
interested about changes in the current audio output level. We will use
this to implement a VU meter:
@code
Listeners <Listener> listeners;
// (Process audio data)
// Calculate output level
float outputLevel = calcOutputLevel ();
// Notify listeners
listeners.call (&Listener::onOutputLevelChanged, outputLevel);
@endcode
To receive notifications, derive from Listener and then add yourself to the
Listeners object using the desired CallQueue.
@code
// We want notifications on the message thread
GuiCallQueue fifo;
struct VUMeter : public Listener, public Component
{
VUMeter () : m_outputLevel (0)
{
listeners.add (this, fifo);
}
~VUMeter ()
{
listeners.remove (this);
}
void onOutputLevelChanged (float outputLevel)
{
// Update our copy of the output level shared state.
m_outputLevel = outputLevel;
// Now trigger a redraw of the control.
repaint ();
}
float m_outputLevel;
};
@endcode
In this example, the VUMeter constructs with the output level set to zero,
and must wait for a notification before it shows up to date data. For a
simple VU meter, this is likely not a problem. But if the shared state
contains complex information, such as dynamically allocated objects with
rich data, then we need a more solid system.
We will add some classes to create a complete robust example of the use of
Listeners to synchronize shared state:
@code
// Handles audio device output.
class AudioDeviceOutput : public AudioIODeviceCallback
{
public:
struct Listener
{
// Sent on every output block.
virtual void onOutputLevelChanged (float outputLevel) { }
};
AudioDeviceOutput () : AudioDeviceOutput ("Audio CallQueue")
{
}
~AudioDeviceOutput ()
{
m_fifo.close ();
}
void addListener (Listener* listener, CallQueue& callQueue)
{
// Acquire read access to the shared state.
SharedData <State>::ConstAccess state (m_state);
// Add the listener.
m_listeners.add (listener, callQueue);
// Queue an update for the listener to receive the initial state.
m_listeners.queue1 (listener,
&Listener::onOutputLevelChanged,
state->outputLevel);
}
void removeListener (Listener* listener)
{
m_listeners.remove (listener);
}
protected:
void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,
float** outputChannelData,
int numOutputChannels,
int numSamples)
{
// Synchronize our call queue. Not needed for this example but
// included here as a best-practice for audio device I/O callbacks.
m_fifo.synchronize ();
// (Process audio data)
// Calculate output level.
float newOutputLevel = calcOutputLevel ();
// Update shared state.
{
SharedData <State>::Access state (m_state);
m_state->outputLevel = newOutputLevel;
}
// Notify listeners.
listeners.call (&Listener::onOutputLevelChanged, newOutputLevel);
}
private:
struct State
{
State () : outputLevel (0) { }
float outputLevel;
};
SharedData <State> m_state;
ManualCallQueue m_fifo;
};
@endcode
Although the rigor demonstrated in the example above is not strictly
required when the shared state consists only of a single float, it
becomes necessary when there are dynamically allocated objects with complex
interactions in the shared state.
@see CallQueue
@class Listeners
@ingroup beast_concurrent
*/
class BEAST_API ListenersBase
{
public:
struct ListenersStructureTag { };
typedef GlobalFifoFreeStore <ListenersStructureTag> AllocatorType;
typedef GlobalFifoFreeStore <ListenersBase> CallAllocatorType;
class Call : public SharedObject,
public AllocatedBy <CallAllocatorType>
{
public:
typedef SharedPtr <Call> Ptr;
virtual void operator () (void* const listener) = 0;
};
private:
typedef unsigned long timestamp_t;
class Group;
typedef List <Group> Groups;
class Proxy;
typedef List <Proxy> Proxies;
class CallWork;
class GroupWork;
class GroupWork1;
// Maintains a list of listeners registered on the same CallQueue
//
class Group : public Groups::Node,
public SharedObject,
public AllocatedBy <AllocatorType>
{
public:
typedef SharedPtr <Group> Ptr;
explicit Group (CallQueue& callQueue);
~Group ();
void add (void* listener, const timestamp_t timestamp,
AllocatorType& allocator);
bool remove (void* listener);
bool contains (void* const listener);
void call (Call* const c, const timestamp_t timestamp);
void queue (Call* const c, const timestamp_t timestamp);
void call1 (Call* const c, const timestamp_t timestamp,
void* const listener);
void queue1 (Call* const c, const timestamp_t timestamp,
void* const listener);
void do_call (Call* const c, const timestamp_t timestamp);
void do_call1 (Call* const c, const timestamp_t timestamp,
void* const listener);
bool empty () const
{
return m_list.empty ();
}
CallQueue& getCallQueue () const
{
return m_fifo;
}
private:
struct Entry;
CallQueue& m_fifo;
List <Entry> m_list;
void* m_listener;
CacheLine::Aligned <ReadWriteMutex> m_mutex;
};
// A Proxy is keyed to a unique pointer-to-member of a
// ListenerClass and is used to consolidate multiple unprocessed
// Calls into a single call to prevent excess messaging. It is up
// to the user of the class to decide when this behavior is appropriate.
//
class Proxy : public Proxies::Node,
public AllocatedBy <AllocatorType>
{
public:
enum
{
maxMemberBytes = 16
};
Proxy (void const* const member, const size_t bytes);
~Proxy ();
void add (Group* group, AllocatorType& allocator);
void remove (Group* group);
void update (Call* const c, const timestamp_t timestamp);
bool match (void const* const member, const size_t bytes) const;
private:
class Work;
struct Entry;
typedef List <Entry> Entries;
char m_member [maxMemberBytes];
const size_t m_bytes;
Entries m_entries;
};
protected:
ListenersBase ();
~ListenersBase ();
inline CallAllocatorType& getCallAllocator ()
{
return *m_callAllocator;
}
void add_void (void* const listener, CallQueue& callQueue);
void remove_void (void* const listener);
void callp (Call::Ptr c);
void queuep (Call::Ptr c);
void call1p_void (void* const listener, Call* c);
void queue1p_void (void* const listener, Call* c);
void updatep (void const* const member,
const size_t bytes, Call::Ptr cp);
private:
Proxy* find_proxy (const void* member, size_t bytes);
private:
Groups m_groups;
Proxies m_proxies;
timestamp_t m_timestamp;
CacheLine::Aligned <ReadWriteMutex> m_groups_mutex;
CacheLine::Aligned <ReadWriteMutex> m_proxies_mutex;
AllocatorType::Ptr m_allocator;
CallAllocatorType::Ptr m_callAllocator;
};
/*============================================================================*/
template <class ListenerClass>
class Listeners : public ListenersBase
{
private:
template <class Functor>
class CallType : public Call
{
public:
CallType (Functor f) : m_f (f)
{
}
void operator () (void* const listener)
{
ListenerClass* object = static_cast <ListenerClass*> (listener);
m_f.operator () (object);
}
private:
Functor m_f;
};
template <class Functor>
inline void callf (Functor f)
{
callp (new (getCallAllocator ()) CallType <Functor> (f));
}
template <class Functor>
inline void queuef (Functor f)
{
queuep (new (getCallAllocator ()) CallType <Functor> (f));
}
inline void call1p (ListenerClass* const listener, Call::Ptr c)
{
call1p_void (listener, c);
}
inline void queue1p (ListenerClass* const listener, Call::Ptr c)
{
queue1p_void (listener, c);
}
template <class Functor>
inline void call1f (ListenerClass* const listener, Functor f)
{
call1p (listener, new (getCallAllocator ()) CallType <Functor> (f));
}
template <class Functor>
inline void queue1f (ListenerClass* const listener, Functor f)
{
queue1p (listener, new (getCallAllocator ()) CallType <Functor> (f));
}
template <class Member, class Functor>
inline void updatef (Member member, Functor f)
{
updatep (reinterpret_cast <void*> (&member), sizeof (Member),
new (getCallAllocator ()) CallType <Functor> (f));
}
public:
/** Add a listener.
The specified listener is associated with the specified CallQueue and
added to the list.
Invariants:
- All other members of Listeners are blocked during add().
- The listener is guaranteed to receive every subsequent call.
- The listener must not already exist in the list.
- Safe to call from any thread.
@param listener The listener to add.
@param callQueue The CallQueue to associate with the listener.
*/
void add (ListenerClass* const listener, CallQueue& callQueue)
{
add_void (listener, callQueue);
}
/** Remove a listener.
The specified listener, which must have been previously added, is removed
from the list. A listener always needs to remove itself before the
associated CallQueue is closed.
Invariants:
- All other members of Listeners are blocked during remove().
- The listener is guaranteed not to receive calls after remove() returns.
- Safe to call from any thread.
@param listener The listener to remove.
*/
void remove (ListenerClass* const listener)
{
remove_void (listener);
}
/** Call a member function on every added listener, on its associated
CallQueue.
A listener's CallQueue will be synchronized if this function is called
from it's associated thread.
Invariants:
- A listener that later removes itself afterwards may not get called.
- Calls from the same thread always execute in order.
- A listener can remove itself even if it has a pending call.
@param mf The member function to call. This may be followed by up to 8
arguments.
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Mf>
inline void call (Mf mf)
{ callf (functional::bind (mf, placeholders::_1)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Mf, class T1>
void call (Mf mf, T1 t1)
{ callf (functional::bind (mf, placeholders::_1, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Mf, class T1, class T2>
void call (Mf mf, T1 t1, T2 t2)
{ callf (functional::bind (mf, placeholders::_1, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Mf, class T1, class T2, class T3>
void call (Mf mf, T1 t1, T2 t2, T3 t3)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Mf, class T1, class T2, class T3, class T4>
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Mf, class T1, class T2, class T3, class T4, class T5>
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
/** Queue a member function on every added listener, without synchronizing.
Operates like call(), but no CallQueue synchronization takes place. This
can be necessary when the call to queue() is made inside a held lock.
@param mf The member function to call. This may be followed by up to 8
arguments.
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Mf>
inline void queue (Mf mf)
{ queuef (functional::bind (mf, placeholders::_1)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Mf, class T1>
void queue (Mf mf, T1 t1)
{ queuef (functional::bind (mf, placeholders::_1, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Mf, class T1, class T2>
void queue (Mf mf, T1 t1, T2 t2)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Mf, class T1, class T2, class T3>
void queue (Mf mf, T1 t1, T2 t2, T3 t3)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Mf, class T1, class T2, class T3, class T4>
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Mf, class T1, class T2, class T3, class T4, class T5>
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
/** Call a member function on every added listener, replacing pending
calls to the same member.
This operates like call(), except that if there are pending unprocessed
calls to the same member function,they will be replaced, with the previous
parameters destroyed normally. This functionality is useful for
high frequency notifications of non critical data, where the recipient
may not catch up often enough. For example, the output level of the
AudioIODeviceCallback in the example is a candidate for the use of
update().
@param mf The member function to call. This may be followed by up to 8
arguments.
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Mf>
inline void update (Mf mf)
{ updatef (mf, functional::bind (mf, placeholders::_1)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Mf, class T1>
void update (Mf mf, T1 t1)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Mf, class T1, class T2>
void update (Mf mf, T1 t1, T2 t2)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Mf, class T1, class T2, class T3>
void update (Mf mf, T1 t1, T2 t2, T3 t3)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Mf, class T1, class T2, class T3, class T4>
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Mf, class T1, class T2, class T3, class T4, class T5>
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
/** Call a member function on a specific listener.
Like call(), except that one listener is targeted only. This is useful when
builing complex behaviors during the addition of a listener, such as
providing an initial state.
@param listener The listener to call.
@param mf The member function to call. This may be followed by up
to 8 arguments.
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Mf>
inline void call1 (ListenerClass* const listener, Mf mf)
{ call1f (listener, functional::bind (mf, placeholders::_1)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Mf, class T1>
void call1 (ListenerClass* const listener, Mf mf, T1 t1)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Mf, class T1, class T2>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Mf, class T1, class T2, class T3>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Mf, class T1, class T2, class T3, class T4>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Mf, class T1, class T2, class T3, class T4, class T5>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
/** Queue a member function on a specific listener.
Like call1(), except that no CallQueue synchronization takes place.
@param listener The listener to call.
@param mf The member function to call. This may be followed by up
to 8 arguments.
*/
/** @{ */
#if BEAST_VARIADIC_MAX >= 1
template <class Mf>
inline void queue1 (ListenerClass* const listener, Mf mf)
{ queue1f (listener, functional::bind (mf, placeholders::_1)); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <class Mf, class T1>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <class Mf, class T1, class T2>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <class Mf, class T1, class T2, class T3>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <class Mf, class T1, class T2, class T3, class T4>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <class Mf, class T1, class T2, class T3, class T4, class T5>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
#endif
/** @} */
};
/** @} */
#endif

View File

@@ -1,54 +0,0 @@
/*============================================================================*/
/*
VFLib: https://github.com/vinniefalco/VFLib
Copyright (C) 2008 by Vinnie Falco <vinnie.falco@gmail.com>
This library contains portions of other open source products covered by
separate licenses. Please see the corresponding source files for specific
terms.
VFLib is provided under the terms of The MIT License (MIT):
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.
*/
/*============================================================================*/
ManualCallQueue::ManualCallQueue (String name)
: CallQueue (name)
{
}
void ManualCallQueue::close ()
{
CallQueue::close ();
}
bool ManualCallQueue::synchronize ()
{
return CallQueue::synchronize ();
}
void ManualCallQueue::signal ()
{
}
void ManualCallQueue::reset ()
{
}

View File

@@ -1,108 +0,0 @@
/*============================================================================*/
/*
VFLib: https://github.com/vinniefalco/VFLib
Copyright (C) 2008 by Vinnie Falco <vinnie.falco@gmail.com>
This library contains portions of other open source products covered by
separate licenses. Please see the corresponding source files for specific
terms.
VFLib is provided under the terms of The MIT License (MIT):
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.
*/
/*============================================================================*/
#ifndef VF_MANUALCALLQUEUE_VFHEADER
#define VF_MANUALCALLQUEUE_VFHEADER
/*============================================================================*/
/**
A CallQueue that requires periodic manual synchronization.
To use this, declare an instance and then place calls into it as usual.
Every so often, you must call synchronize() from the thread you want to
associate with the queue. Typically this is done within an
AudioIODeviceCallback:
@code
class AudioIODeviceCallbackWithCallQueue
: public AudioIODeviceCallback
, public CallQueue
{
public:
AudioIODeviceCallbackWithCallQueue () : m_fifo ("Audio CallQueue")
{
}
void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,
float** outputChannelData,
int numOutputChannels,
int numSamples)
{
CallQueue::synchronize ();
// do audio i/o
}
void signal () { } // No action required
void reset () { } // No action required
};
@endcode
The close() function is provided for diagnostics. Call it as early as
possible based on the exit or shutdown logic of your application. If calls
are put into the queue after it is closed, it will generate an exception so
you can track it down.
@see CallQueue
@ingroup vf_concurrent
*/
class ManualCallQueue : public CallQueue
{
public:
/** Create a ManualCallQueue.
@param name A string used to help identify the associated
thread for debugging.
*/
explicit ManualCallQueue (String name);
/** Close the queue. If calls are placed into a closed queue, an exception
is thrown.
*/
void close ();
/** Synchronize the queue by calling all pending functors.
@return `true` if any functors were called.
*/
bool synchronize ();
private:
void signal ();
void reset ();
};
#endif

View File

@@ -1,283 +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.
*/
//==============================================================================
ThreadWithCallQueue::ThreadWithCallQueue (String name)
: CallQueue (name)
, m_thread (name)
, m_entryPoints (nullptr)
, m_calledStart (false)
, m_calledStop (false)
, m_shouldStop (false)
{
}
ThreadWithCallQueue::~ThreadWithCallQueue ()
{
stop (true);
}
ThreadWithCallQueue::EntryPoints* ThreadWithCallQueue::getDefaultEntryPoints () noexcept
{
static EntryPoints entryPoints;
return &entryPoints;
}
void ThreadWithCallQueue::start (EntryPoints* const entryPoints)
{
{
// This is mostly for diagnostics
// TODO: Atomic flag for this whole thing
CriticalSection::ScopedLockType lock (m_mutex);
// start() MUST be called.
bassert (!m_calledStart);
m_calledStart = true;
}
m_entryPoints = entryPoints;
m_thread.start (this);
}
void ThreadWithCallQueue::stop (bool const wait)
{
// can't call stop(true) from within a thread function
bassert (!wait || !m_thread.isTheCurrentThread ());
{
CriticalSection::ScopedLockType lock (m_mutex);
// start() MUST be called.
bassert (m_calledStart);
// TODO: Atomic for this
if (!m_calledStop)
{
m_calledStop = true;
{
CriticalSection::ScopedUnlockType unlock (m_mutex); // getting fancy
call (&ThreadWithCallQueue::doStop, this);
// in theory something could slip in here
close ();
}
}
}
if (wait)
m_thread.join ();
}
// Should be called periodically by the idle function.
// There are three possible results:
//
// #1 Returns false. The idle function may continue or return.
// #2 Returns true. The idle function should return as soon as possible.
// #3 Throws a Thread::Interruption exception.
//
// If interruptionPoint returns true or throws, it must
// not be called again before the thread has the opportunity to reset.
//
bool ThreadWithCallQueue::interruptionPoint ()
{
return m_thread.interruptionPoint ();
}
// Interrupts the idle function by queueing a call that does nothing.
void ThreadWithCallQueue::interrupt ()
{
//call (&ThreadWithCallQueue::doNothing);
m_thread.interrupt();
}
void ThreadWithCallQueue::doNothing ()
{
// Intentionally empty
}
void ThreadWithCallQueue::signal ()
{
m_thread.interrupt ();
}
void ThreadWithCallQueue::reset ()
{
}
void ThreadWithCallQueue::doStop ()
{
m_shouldStop = true;
}
void ThreadWithCallQueue::threadRun ()
{
m_entryPoints->threadInit ();
for (;;)
{
CallQueue::synchronize ();
if (m_shouldStop)
break;
bool interrupted = m_entryPoints->threadIdle ();
if (!interrupted)
interrupted = interruptionPoint ();
if (!interrupted)
m_thread.wait ();
}
m_entryPoints->threadExit ();
}
//------------------------------------------------------------------------------
class ThreadWithCallQueueTests : public UnitTest
{
public:
enum
{
callsPerThread = 20000
};
struct TestThread : ThreadWithCallQueue, ThreadWithCallQueue::EntryPoints
{
explicit TestThread (int id)
: ThreadWithCallQueue ("#" + String::fromNumber (id))
, interruptedOnce (false)
{
start (this);
}
bool interruptedOnce;
void threadInit ()
{
}
void threadExit ()
{
}
bool threadIdle ()
{
bool interrupted;
String s;
for (;;)
{
interrupted = interruptionPoint ();
if (interrupted)
{
interruptedOnce = true;
break;
}
s = s + String::fromNumber (m_random.nextInt ());
if (s.length () > 100)
s = String::empty;
}
bassert (interrupted);
return interrupted;
}
Random m_random;
};
void func1 ()
{
}
void func2 ()
{
}
void func3 ()
{
}
void testThreads (int nThreads)
{
beginTestCase (String::fromNumber (nThreads) + " threads");
OwnedArray <TestThread> threads;
threads.ensureStorageAllocated (nThreads);
for (int i = 0; i < nThreads; ++i)
threads.add (new TestThread (i + 1));
for (int i = 0; i < 100000; ++i)
{
int const n (random().nextInt (threads.size()));
int const f (random().nextInt (3));
switch (f)
{
default:
bassertfalse;
#if 0
case 0: threads[n]->call (&ThreadWithCallQueueTests::func1, this); break;
case 1: threads[n]->call (&ThreadWithCallQueueTests::func2, this); break;
case 2: threads[n]->call (&ThreadWithCallQueueTests::func3, this); break;
#else
case 0: threads[n]->interrupt(); break;
case 1: threads[n]->interrupt(); break;
case 2: threads[n]->interrupt(); break;
#endif
};
}
#if 0
for (int i = 0; i < threads.size(); ++i)
{
expect (threads[i]->interruptedOnce);
}
#endif
#if 1
for (int i = 0; i < threads.size(); ++i)
threads[i]->stop (false);
for (int i = 0; i < threads.size(); ++i)
threads[i]->stop (true);
#endif
pass ();
}
void runTest ()
{
testThreads (5);
testThreads (50);
}
ThreadWithCallQueueTests () : UnitTest ("ThreadWithCallQueue", "beast")
{
}
};
static ThreadWithCallQueueTests threadWithCallQueueTests;

View File

@@ -1,155 +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_THREADWITHCALLQUEUE_H_INCLUDED
#define BEAST_THREADWITHCALLQUEUE_H_INCLUDED
/** An InterruptibleThread with a CallQueue.
This combines an InterruptibleThread with a CallQueue, allowing functors to
be queued for asynchronous execution on the thread.
The thread runs an optional user-defined idle function, which must regularly
check for an interruption using the InterruptibleThread interface. When an
interruption is signaled, the idle function returns and the CallQueue is
synchronized. Then, the idle function is resumed.
When the ThreadWithCallQueue first starts up, an optional user-defined
initialization function is executed on the thread. When the thread exits,
a user-defined exit function may be executed on the thread.
@see CallQueue
@ingroup beast_concurrent
*/
class BEAST_API ThreadWithCallQueue
: public CallQueue
, private InterruptibleThread::EntryPoint
, LeakChecked <ThreadWithCallQueue>
{
public:
/** Entry points for a ThreadWithCallQueue.
*/
class EntryPoints
{
public:
virtual ~EntryPoints () { }
virtual void threadInit () { }
virtual void threadExit () { }
virtual bool threadIdle ()
{
bool interrupted = false;
return interrupted;
}
};
/** Create a thread.
@param name The name of the InterruptibleThread and CallQueue, used
for diagnostics when debugging.
*/
explicit ThreadWithCallQueue (String name);
/** Retrieve the default entry points.
The default entry points do nothing.
*/
static EntryPoints* getDefaultEntryPoints () noexcept;
/** Destroy a ThreadWithCallQueue.
If the thread is still running it is stopped. The destructor blocks
until the thread exits cleanly.
*/
~ThreadWithCallQueue ();
/** Start the thread, with optional entry points.
If `entryPoints` is specified then the thread runs using those
entry points. If ommitted, the default entry simply do nothing.
This is useful for creating a thread whose sole activities are
performed through the call queue.
@param entryPoints An optional pointer to @ref EntryPoints.
*/
void start (EntryPoints* const entryPoints = getDefaultEntryPoints ());
/* Stop the thread.
Stops the thread and optionally wait until it exits. It is safe to call
this function at any time and as many times as desired.
After a call to stop () the CallQueue is closed, and attempts to queue new
functors will throw a runtime exception. Existing functors will still
execute.
Any listeners registered on the CallQueue need to be removed
before stop is called
@invariant The caller is not on the associated thread.
@param wait `true` if the function should wait until the thread exits
before returning.
*/
void stop (bool const wait);
/** Determine if the thread needs interruption.
Should be called periodically by the idle function. If interruptionPoint
returns true or throws, it must not be called again until the idle function
returns and is re-entered.
@invariant No previous calls to interruptionPoint() made after the idle
function entry point returned `true`.
@return `false` if the idle function may continue, or `true` if the
idle function must return as soon as possible.
*/
bool interruptionPoint ();
/* Interrupts the idle function.
*/
void interrupt ();
private:
static void doNothing ();
void signal ();
void reset ();
void doStop ();
void threadRun ();
private:
InterruptibleThread m_thread;
EntryPoints* m_entryPoints;
bool m_calledStart;
bool m_calledStop;
bool m_shouldStop;
CriticalSection m_mutex;
};
#endif