mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Beast class refactor
This commit is contained in:
@@ -133,6 +133,9 @@
|
|||||||
<ClInclude Include="..\..\beast\Net.h" />
|
<ClInclude Include="..\..\beast\Net.h" />
|
||||||
<ClInclude Include="..\..\beast\net\IPEndpoint.h" />
|
<ClInclude Include="..\..\beast\net\IPEndpoint.h" />
|
||||||
<ClInclude Include="..\..\beast\SafeBool.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\StaticAssert.h" />
|
||||||
<ClInclude Include="..\..\beast\Strings.h" />
|
<ClInclude Include="..\..\beast\Strings.h" />
|
||||||
<ClInclude Include="..\..\beast\strings\CharacterFunctions.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\ScopedValueSetter.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\containers\SortedSet.h" />
|
<ClInclude Include="..\..\modules\beast_core\containers\SortedSet.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\containers\SparseSet.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\containers\Variant.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\diagnostic\FatalError.h" />
|
<ClInclude Include="..\..\modules\beast_core\diagnostic\FatalError.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\diagnostic\FPUFlags.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\Random.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\maths\Range.h" />
|
<ClInclude Include="..\..\modules\beast_core\maths\Range.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\maths\uint24.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\AtomicCounter.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\AtomicFlag.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\AtomicFlag.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\AtomicPointer.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\AtomicPointer.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\AtomicState.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\AtomicState.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\CacheLine.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\MemoryAlignment.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\MemoryBlock.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\MemoryBlock.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\OptionalScopedPointer.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\RecycledObjectPool.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\SharedFunction.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\SharedFunction.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\SharedObject.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\SharedSingleton.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h" />
|
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\SharedPtr.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\ThreadPool.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\threads\TimeSliceThread.h" />
|
<ClInclude Include="..\..\modules\beast_core\threads\TimeSliceThread.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\threads\WaitableEvent.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\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\Semaphore.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\ServiceQueue.h" />
|
<ClInclude Include="..\..\modules\beast_core\thread\ServiceQueue.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\Stoppable.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\Workers.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\detail\ScopedLock.h" />
|
<ClInclude Include="..\..\modules\beast_core\thread\detail\ScopedLock.h" />
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\detail\TrackedMutex.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|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\diagnostic\Assert.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|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|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\memory\MemoryBlock.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\memory\StaticObject.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|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|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\thread\DeadlineTimer.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\thread\Semaphore.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|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)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\thread\Workers.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -285,6 +285,12 @@
|
|||||||
<Filter Include="beast\config\stdlib">
|
<Filter Include="beast\config\stdlib">
|
||||||
<UniqueIdentifier>{7243e5e5-ad7e-4d81-8444-d545919e850c}</UniqueIdentifier>
|
<UniqueIdentifier>{7243e5e5-ad7e-4d81-8444-d545919e850c}</UniqueIdentifier>
|
||||||
</Filter>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\modules\beast_core\beast_core.h">
|
<ClInclude Include="..\..\modules\beast_core\beast_core.h">
|
||||||
@@ -377,9 +383,6 @@
|
|||||||
<ClInclude Include="..\..\modules\beast_core\memory\OptionalScopedPointer.h">
|
<ClInclude Include="..\..\modules\beast_core\memory\OptionalScopedPointer.h">
|
||||||
<Filter>beast_core\memory</Filter>
|
<Filter>beast_core\memory</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\ScopedPointer.h">
|
|
||||||
<Filter>beast_core\memory</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h">
|
<ClInclude Include="..\..\modules\beast_core\memory\WeakReference.h">
|
||||||
<Filter>beast_core\memory</Filter>
|
<Filter>beast_core\memory</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -704,9 +707,6 @@
|
|||||||
<ClInclude Include="..\..\modules\beast_core\maths\uint24.h">
|
<ClInclude Include="..\..\modules\beast_core\maths\uint24.h">
|
||||||
<Filter>beast_core\maths</Filter>
|
<Filter>beast_core\maths</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\modules\beast_core\memory\ContainerDeletePolicy.h">
|
|
||||||
<Filter>beast_core\memory</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\modules\beast_core\diagnostic\MeasureFunctionCallTime.h">
|
<ClInclude Include="..\..\modules\beast_core\diagnostic\MeasureFunctionCallTime.h">
|
||||||
<Filter>beast_core\diagnostic</Filter>
|
<Filter>beast_core\diagnostic</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -737,51 +737,15 @@
|
|||||||
<ClInclude Include="..\..\modules\beast_core\thread\DeadlineTimer.h">
|
<ClInclude Include="..\..\modules\beast_core\thread\DeadlineTimer.h">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\modules\beast_core\thread\Semaphore.h">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.h">
|
|
||||||
<Filter>beast_core\thread</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\modules\beast_core\thread\Workers.h">
|
<ClInclude Include="..\..\modules\beast_core\thread\Workers.h">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\modules\beast_core\maths\Math.h">
|
<ClInclude Include="..\..\modules\beast_core\maths\Math.h">
|
||||||
<Filter>beast_core\maths</Filter>
|
<Filter>beast_core\maths</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\modules\beast_asio\sockets\SocketWrapperStrand.h">
|
||||||
<Filter>beast_asio\sockets</Filter>
|
<Filter>beast_asio\sockets</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -818,9 +782,6 @@
|
|||||||
<ClInclude Include="..\..\modules\beast_core\system\SystemStats.h">
|
<ClInclude Include="..\..\modules\beast_core\system\SystemStats.h">
|
||||||
<Filter>beast_core\system</Filter>
|
<Filter>beast_core\system</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\modules\beast_core\containers\DynamicList.h">
|
|
||||||
<Filter>beast_core\containers</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\beast\intrusive\ForwardList.h">
|
<ClInclude Include="..\..\beast\intrusive\ForwardList.h">
|
||||||
<Filter>beast\intrusive</Filter>
|
<Filter>beast\intrusive</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1245,6 +1206,15 @@
|
|||||||
<ClInclude Include="..\..\beast\config\SelectStdlibConfig.h">
|
<ClInclude Include="..\..\beast\config\SelectStdlibConfig.h">
|
||||||
<Filter>beast\config</Filter>
|
<Filter>beast\config</Filter>
|
||||||
</ClInclude>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\modules\beast_core\containers\AbstractFifo.cpp">
|
<ClCompile Include="..\..\modules\beast_core\containers\AbstractFifo.cpp">
|
||||||
@@ -1580,39 +1550,12 @@
|
|||||||
<ClCompile Include="..\..\modules\beast_core\thread\DeadlineTimer.cpp">
|
<ClCompile Include="..\..\modules\beast_core\thread\DeadlineTimer.cpp">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_core\thread\Semaphore.cpp">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\modules\beast_core\thread\ThreadWithCallQueue.cpp">
|
|
||||||
<Filter>beast_core\thread</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\modules\beast_core\thread\Workers.cpp">
|
<ClCompile Include="..\..\modules\beast_core\thread\Workers.cpp">
|
||||||
<Filter>beast_core\thread</Filter>
|
<Filter>beast_core\thread</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\modules\beast_asio\basics\SSLContext.cpp">
|
||||||
<Filter>beast_asio\basics</Filter>
|
<Filter>beast_asio\basics</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -1622,9 +1565,6 @@
|
|||||||
<ClCompile Include="..\..\modules\beast_core\system\SystemStats.cpp">
|
<ClCompile Include="..\..\modules\beast_core\system\SystemStats.cpp">
|
||||||
<Filter>beast_core\system</Filter>
|
<Filter>beast_core\system</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\modules\beast_core\containers\DynamicList.cpp">
|
|
||||||
<Filter>beast_core\containers</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\modules\beast_asio\async\SharedHandler.cpp">
|
<ClCompile Include="..\..\modules\beast_asio\async\SharedHandler.cpp">
|
||||||
<Filter>beast_asio\async</Filter>
|
<Filter>beast_asio\async</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -17,27 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
namespace
|
#ifndef BEAST_SMARTPTR_H_INCLUDED
|
||||||
{
|
#define BEAST_SMARTPTR_H_INCLUDED
|
||||||
|
|
||||||
// Size of a page
|
#include "Config.h"
|
||||||
//
|
|
||||||
static const size_t globalPageBytes = 8 * 1024;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalPagedFreeStore::GlobalPagedFreeStore ()
|
|
||||||
: m_allocator (globalPageBytes)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalPagedFreeStore::~GlobalPagedFreeStore ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalPagedFreeStore::Ptr GlobalPagedFreeStore::getInstance ()
|
|
||||||
{
|
|
||||||
return SharedSingleton <GlobalPagedFreeStore>::getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#include "smart_ptr/ContainerDeletePolicy.h"
|
||||||
|
#include "smart_ptr/ScopedPointer.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,8 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_CONTAINERDELETEPOLICY_H_INCLUDED
|
#ifndef BEAST_SMARTPTR_CONTAINERDELETEPOLICY_H_INCLUDED
|
||||||
#define BEAST_CONTAINERDELETEPOLICY_H_INCLUDED
|
#define BEAST_SMARTPTR_CONTAINERDELETEPOLICY_H_INCLUDED
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
/** The DeletePolicy provides a way to destroy objects stored in containers.
|
/** The DeletePolicy provides a way to destroy objects stored in containers.
|
||||||
|
|
||||||
@@ -45,4 +47,6 @@ struct ContainerDeletePolicy
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -21,8 +21,14 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_SCOPEDPOINTER_H_INCLUDED
|
#ifndef BEAST_SMARTPTR_SCOPEDPOINTER_H_INCLUDED
|
||||||
#define BEAST_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); }
|
void deleteAndZero (ScopedPointer<Type>&) { static_bassert (sizeof (Type) == 12345); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // BEAST_SCOPEDPOINTER_H_INCLUDED
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ namespace beast {
|
|||||||
#include "protocol/HandshakeDetectLogicPROXY.cpp"
|
#include "protocol/HandshakeDetectLogicPROXY.cpp"
|
||||||
|
|
||||||
# include "http/HTTPParserImpl.h"
|
# include "http/HTTPParserImpl.h"
|
||||||
#include "http/HTTPParser.cpp"
|
|
||||||
#include "http/HTTPClientType.cpp"
|
#include "http/HTTPClientType.cpp"
|
||||||
#include "http/HTTPField.cpp"
|
#include "http/HTTPField.cpp"
|
||||||
#include "http/HTTPHeaders.cpp"
|
#include "http/HTTPHeaders.cpp"
|
||||||
@@ -61,3 +60,5 @@ namespace beast {
|
|||||||
#include "system/BoostUnitTests.cpp"
|
#include "system/BoostUnitTests.cpp"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "http/HTTPParser.cpp"
|
||||||
|
|||||||
@@ -89,10 +89,11 @@ namespace beast {
|
|||||||
# include "http/HTTPMessage.h"
|
# include "http/HTTPMessage.h"
|
||||||
# include "http/HTTPRequest.h"
|
# include "http/HTTPRequest.h"
|
||||||
# include "http/HTTPResponse.h"
|
# include "http/HTTPResponse.h"
|
||||||
# include "http/HTTPParser.h"
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# include "http/HTTPParser.h"
|
||||||
|
|
||||||
#include "http/HTTPClientType.h"
|
#include "http/HTTPClientType.h"
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
HTTPParser::HTTPParser (Type type)
|
HTTPParser::HTTPParser (Type type)
|
||||||
: m_type (type)
|
: m_type (type)
|
||||||
, m_impl (new HTTPParserImpl (
|
, m_impl (new HTTPParserImpl (
|
||||||
@@ -102,3 +104,5 @@ SharedPtr <HTTPResponse> const& HTTPParser::response ()
|
|||||||
|
|
||||||
return m_response;
|
return m_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#ifndef BEAST_ASIO_HTTPPARSER_H_INCLUDED
|
#ifndef BEAST_ASIO_HTTPPARSER_H_INCLUDED
|
||||||
#define BEAST_ASIO_HTTPPARSER_H_INCLUDED
|
#define BEAST_ASIO_HTTPPARSER_H_INCLUDED
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
class HTTPParserImpl;
|
class HTTPParserImpl;
|
||||||
|
|
||||||
/** A parser for HTTPRequest and HTTPResponse objects. */
|
/** A parser for HTTPRequest and HTTPResponse objects. */
|
||||||
@@ -82,4 +84,6 @@ private:
|
|||||||
SharedPtr <HTTPResponse> m_response;
|
SharedPtr <HTTPResponse> m_response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ namespace beast
|
|||||||
#include "containers/NamedValueSet.cpp"
|
#include "containers/NamedValueSet.cpp"
|
||||||
#include "containers/PropertySet.cpp"
|
#include "containers/PropertySet.cpp"
|
||||||
#include "containers/Variant.cpp"
|
#include "containers/Variant.cpp"
|
||||||
#include "containers/DynamicList.cpp"
|
|
||||||
|
|
||||||
#include "diagnostic/FatalError.cpp"
|
#include "diagnostic/FatalError.cpp"
|
||||||
#include "diagnostic/FPUFlags.cpp"
|
#include "diagnostic/FPUFlags.cpp"
|
||||||
@@ -162,10 +161,6 @@ namespace beast
|
|||||||
#include "maths/Random.cpp"
|
#include "maths/Random.cpp"
|
||||||
|
|
||||||
#include "memory/MemoryBlock.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 "memory/StaticObject.cpp"
|
||||||
|
|
||||||
#include "misc/Main.cpp"
|
#include "misc/Main.cpp"
|
||||||
@@ -197,13 +192,8 @@ namespace beast
|
|||||||
|
|
||||||
#include "thread/impl/TrackedMutex.cpp"
|
#include "thread/impl/TrackedMutex.cpp"
|
||||||
#include "thread/DeadlineTimer.cpp"
|
#include "thread/DeadlineTimer.cpp"
|
||||||
#include "thread/InterruptibleThread.cpp"
|
|
||||||
#include "thread/Stoppable.cpp"
|
#include "thread/Stoppable.cpp"
|
||||||
#include "thread/Semaphore.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 "thread/Workers.cpp"
|
||||||
|
|
||||||
#include "threads/ChildProcess.cpp"
|
#include "threads/ChildProcess.cpp"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
// New header-only library modeled more closely according to boost
|
// New header-only library modeled more closely according to boost
|
||||||
#include "../../beast/CStdInt.h"
|
#include "../../beast/CStdInt.h"
|
||||||
|
#include "../../beast/SmartPtr.h"
|
||||||
#include "../../beast/StaticAssert.h"
|
#include "../../beast/StaticAssert.h"
|
||||||
#include "../../beast/Uncopyable.h"
|
#include "../../beast/Uncopyable.h"
|
||||||
#include "../../beast/Atomic.h"
|
#include "../../beast/Atomic.h"
|
||||||
@@ -155,7 +156,6 @@ class FileOutputStream;
|
|||||||
#include "thread/TrackedMutex.h"
|
#include "thread/TrackedMutex.h"
|
||||||
#include "diagnostic/FatalError.h"
|
#include "diagnostic/FatalError.h"
|
||||||
#include "text/LexicalCast.h"
|
#include "text/LexicalCast.h"
|
||||||
#include "memory/ContainerDeletePolicy.h"
|
|
||||||
#include "maths/Math.h"
|
#include "maths/Math.h"
|
||||||
#include "maths/uint24.h"
|
#include "maths/uint24.h"
|
||||||
#include "logging/Logger.h"
|
#include "logging/Logger.h"
|
||||||
@@ -180,8 +180,6 @@ class FileOutputStream;
|
|||||||
#include "containers/SortedSet.h"
|
#include "containers/SortedSet.h"
|
||||||
#include "maths/Range.h"
|
#include "maths/Range.h"
|
||||||
#include "containers/SparseSet.h"
|
#include "containers/SparseSet.h"
|
||||||
# include "containers/DynamicList.h"
|
|
||||||
#include "memory/ScopedPointer.h"
|
|
||||||
#include "files/DirectoryIterator.h"
|
#include "files/DirectoryIterator.h"
|
||||||
#include "streams/InputStream.h"
|
#include "streams/InputStream.h"
|
||||||
#include "files/FileInputStream.h"
|
#include "files/FileInputStream.h"
|
||||||
@@ -242,21 +240,8 @@ class FileOutputStream;
|
|||||||
|
|
||||||
#include "thread/DeadlineTimer.h"
|
#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/Semaphore.h"
|
||||||
#include "thread/InterruptibleThread.h"
|
|
||||||
#include "thread/Stoppable.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"
|
#include "thread/Workers.h"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -20,18 +20,6 @@
|
|||||||
#ifndef BEAST_STATICOBJECT_H_INCLUDED
|
#ifndef BEAST_STATICOBJECT_H_INCLUDED
|
||||||
#define 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
|
// Spec: N2914=09-0104
|
||||||
//
|
//
|
||||||
// [3.6.2] Initialization of non-local objects
|
// [3.6.2] Initialization of non-local objects
|
||||||
@@ -40,84 +28,8 @@
|
|||||||
// duration (3.7.2) shall be zero-initialized (8.5) before any
|
// duration (3.7.2) shall be zero-initialized (8.5) before any
|
||||||
// other initialization takes place.
|
// 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
|
namespace detail {
|
||||||
{
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
|
|
||||||
extern void staticObjectWait (std::size_t n);
|
extern void staticObjectWait (std::size_t n);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
@@ -30,8 +30,9 @@ public:
|
|||||||
The listener is called on an auxiliary thread. It is suggested
|
The listener is called on an auxiliary thread. It is suggested
|
||||||
not to perform any time consuming operations during the call.
|
not to perform any time consuming operations during the call.
|
||||||
*/
|
*/
|
||||||
// VFALCO TODO Allow construction with a specific ThreadWithCallQueue&
|
// VFALCO TODO Perhaps allow construction using a ServiceQueue to use
|
||||||
// on which to notify the listener.
|
// for notifications.
|
||||||
|
//
|
||||||
class Listener
|
class Listener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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 ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user