12f083884Ss.makeev_local // The MIT License (MIT)
22f083884Ss.makeev_local //
32f083884Ss.makeev_local // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
42f083884Ss.makeev_local //
52f083884Ss.makeev_local // Permission is hereby granted, free of charge, to any person obtaining a copy
62f083884Ss.makeev_local // of this software and associated documentation files (the "Software"), to deal
72f083884Ss.makeev_local // in the Software without restriction, including without limitation the rights
82f083884Ss.makeev_local // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92f083884Ss.makeev_local // copies of the Software, and to permit persons to whom the Software is
102f083884Ss.makeev_local // furnished to do so, subject to the following conditions:
112f083884Ss.makeev_local //
122f083884Ss.makeev_local // The above copyright notice and this permission notice shall be included in
132f083884Ss.makeev_local // all copies or substantial portions of the Software.
142f083884Ss.makeev_local //
152f083884Ss.makeev_local // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162f083884Ss.makeev_local // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172f083884Ss.makeev_local // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
182f083884Ss.makeev_local // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
192f083884Ss.makeev_local // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
202f083884Ss.makeev_local // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
212f083884Ss.makeev_local // THE SOFTWARE.
222f083884Ss.makeev_local
232f083884Ss.makeev_local #include "Tests.h"
242f083884Ss.makeev_local #include <UnitTest++.h>
252f083884Ss.makeev_local #include <MTScheduler.h>
263a3d248dSs.makeev_local #include <MTStaticVector.h>
272f083884Ss.makeev_local
SUITE(SimpleTests)282f083884Ss.makeev_local SUITE(SimpleTests)
292f083884Ss.makeev_local {
302f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
312f083884Ss.makeev_local struct SimpleTask
322f083884Ss.makeev_local {
33b23bdf5aSs.makeev_local MT_DECLARE_TASK(SimpleTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
342f083884Ss.makeev_local
352f083884Ss.makeev_local static const int sourceData = 0xFF33FF;
362f083884Ss.makeev_local int resultData;
372f083884Ss.makeev_local
382f083884Ss.makeev_local SimpleTask() : resultData(0) {}
392f083884Ss.makeev_local
402f083884Ss.makeev_local void Do(MT::FiberContext&)
412f083884Ss.makeev_local {
422f083884Ss.makeev_local resultData = sourceData;
432f083884Ss.makeev_local }
442f083884Ss.makeev_local
452f083884Ss.makeev_local int GetSourceData()
462f083884Ss.makeev_local {
472f083884Ss.makeev_local return sourceData;
482f083884Ss.makeev_local }
492f083884Ss.makeev_local };
502f083884Ss.makeev_local
512f083884Ss.makeev_local // Checks one simple task
522f083884Ss.makeev_local TEST(RunOneSimpleTask)
532f083884Ss.makeev_local {
542f083884Ss.makeev_local MT::TaskScheduler scheduler;
552f083884Ss.makeev_local
562f083884Ss.makeev_local SimpleTask task;
572f083884Ss.makeev_local scheduler.RunAsync(MT::TaskGroup::Default(), &task, 1);
582f083884Ss.makeev_local
592f083884Ss.makeev_local CHECK(scheduler.WaitAll(1000));
602f083884Ss.makeev_local CHECK_EQUAL(task.GetSourceData(), task.resultData);
612f083884Ss.makeev_local }
622f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
632f083884Ss.makeev_local
642f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
652f083884Ss.makeev_local struct ALotOfTasks
662f083884Ss.makeev_local {
67b23bdf5aSs.makeev_local MT_DECLARE_TASK(ALotOfTasks, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
682f083884Ss.makeev_local
692f083884Ss.makeev_local MT::Atomic32<int32>* counter;
702f083884Ss.makeev_local
712f083884Ss.makeev_local void Do(MT::FiberContext&)
722f083884Ss.makeev_local {
732f083884Ss.makeev_local counter->IncFetch();
74*3cb1fd8eSs.makeev_local MT::SpinSleepMilliSeconds(1);
752f083884Ss.makeev_local }
762f083884Ss.makeev_local };
772f083884Ss.makeev_local
782f083884Ss.makeev_local // Checks one simple task
792f083884Ss.makeev_local TEST(ALotOfTasks)
802f083884Ss.makeev_local {
812f083884Ss.makeev_local MT::TaskScheduler scheduler;
822f083884Ss.makeev_local
832f083884Ss.makeev_local MT::Atomic32<int32> counter;
842f083884Ss.makeev_local
852f083884Ss.makeev_local static const int TASK_COUNT = 1000;
862f083884Ss.makeev_local
872f083884Ss.makeev_local ALotOfTasks tasks[TASK_COUNT];
882f083884Ss.makeev_local
892f083884Ss.makeev_local for (size_t i = 0; i < MT_ARRAY_SIZE(tasks); ++i)
902f083884Ss.makeev_local tasks[i].counter = &counter;
912f083884Ss.makeev_local
922f083884Ss.makeev_local scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
932f083884Ss.makeev_local
942f083884Ss.makeev_local int timeout = (TASK_COUNT / scheduler.GetWorkersCount()) * 2000;
952f083884Ss.makeev_local
962f083884Ss.makeev_local CHECK(scheduler.WaitGroup(MT::TaskGroup::Default(), timeout));
972f083884Ss.makeev_local CHECK_EQUAL(TASK_COUNT, counter.Load());
982f083884Ss.makeev_local }
993a3d248dSs.makeev_local
1003a3d248dSs.makeev_local
1013a3d248dSs.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1023a3d248dSs.makeev_local
1033a3d248dSs.makeev_local
1043a3d248dSs.makeev_local struct WorkerThreadState
1053a3d248dSs.makeev_local {
1063a3d248dSs.makeev_local uint32 counterPhase0;
1073a3d248dSs.makeev_local uint32 counterPhase1;
1083a3d248dSs.makeev_local
1093a3d248dSs.makeev_local WorkerThreadState()
1103a3d248dSs.makeev_local {
11144f7e53cSs.makeev_local Reset();
11244f7e53cSs.makeev_local }
11344f7e53cSs.makeev_local
11444f7e53cSs.makeev_local void Reset()
11544f7e53cSs.makeev_local {
1163a3d248dSs.makeev_local counterPhase0 = 0;
1173a3d248dSs.makeev_local counterPhase1 = 0;
1183a3d248dSs.makeev_local }
1193a3d248dSs.makeev_local };
1203a3d248dSs.makeev_local
1213a3d248dSs.makeev_local
1223a3d248dSs.makeev_local WorkerThreadState workerStates[64];
1233a3d248dSs.makeev_local
1243a3d248dSs.makeev_local uint32 TASK_COUNT_PER_WORKER = 0;
1253a3d248dSs.makeev_local
1263a3d248dSs.makeev_local MT::Atomic32<uint32> finishedTaskCount;
1273a3d248dSs.makeev_local
1283a3d248dSs.makeev_local struct YieldTask
1293a3d248dSs.makeev_local {
1303a3d248dSs.makeev_local MT::Atomic32<uint32> counter;
1313a3d248dSs.makeev_local
1323a3d248dSs.makeev_local MT_DECLARE_TASK(YieldTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
1333a3d248dSs.makeev_local
1343a3d248dSs.makeev_local YieldTask()
1353a3d248dSs.makeev_local {
1363a3d248dSs.makeev_local counter.Store(0);
1373a3d248dSs.makeev_local }
1383a3d248dSs.makeev_local
1393a3d248dSs.makeev_local
1403a3d248dSs.makeev_local volatile WorkerThreadState* GetWorkerState( volatile uint32 workerIndex) volatile
1413a3d248dSs.makeev_local {
1423a3d248dSs.makeev_local MT_ASSERT(workerIndex < MT_ARRAY_SIZE(workerStates), "Invalid worker index");
1433a3d248dSs.makeev_local volatile WorkerThreadState& state = workerStates[workerIndex];
1443a3d248dSs.makeev_local return &state;
1453a3d248dSs.makeev_local }
1463a3d248dSs.makeev_local
1473a3d248dSs.makeev_local void Do(MT::FiberContext& context)
1483a3d248dSs.makeev_local {
1493a3d248dSs.makeev_local volatile WorkerThreadState* state0 = GetWorkerState( context.GetThreadContext()->workerIndex );
1503a3d248dSs.makeev_local
1513a3d248dSs.makeev_local // phase 0
1523a3d248dSs.makeev_local CHECK_EQUAL((uint32)1, counter.IncFetch());
1533a3d248dSs.makeev_local state0->counterPhase0++;
1543a3d248dSs.makeev_local context.Yield();
1553a3d248dSs.makeev_local
1563a3d248dSs.makeev_local // worker index can be changed after yield, get actual index
1573a3d248dSs.makeev_local volatile WorkerThreadState* state1 = GetWorkerState( context.GetThreadContext()->workerIndex );
1583a3d248dSs.makeev_local
1593a3d248dSs.makeev_local //I check that all the tasks (on this worker) have passed phase0 before executing phase1
1603a3d248dSs.makeev_local CHECK_EQUAL(TASK_COUNT_PER_WORKER, state1->counterPhase0);
1613a3d248dSs.makeev_local
1623a3d248dSs.makeev_local // phase 1
1633a3d248dSs.makeev_local CHECK_EQUAL((uint32)2, counter.IncFetch());
1643a3d248dSs.makeev_local state1->counterPhase1++;
1653a3d248dSs.makeev_local
1663a3d248dSs.makeev_local finishedTaskCount.IncFetch();
1673a3d248dSs.makeev_local }
1683a3d248dSs.makeev_local };
1693a3d248dSs.makeev_local
1703a3d248dSs.makeev_local
1713a3d248dSs.makeev_local TEST(YieldTasks)
1723a3d248dSs.makeev_local {
1733a3d248dSs.makeev_local // Disable task stealing (for testing purposes only)
1743a3d248dSs.makeev_local #ifdef MT_INSTRUMENTED_BUILD
1753a3d248dSs.makeev_local MT::TaskScheduler scheduler(0, nullptr, nullptr, MT::TaskStealingMode::DISABLED);
1763a3d248dSs.makeev_local #else
1773a3d248dSs.makeev_local MT::TaskScheduler scheduler(0, nullptr, MT::TaskStealingMode::DISABLED);
1783a3d248dSs.makeev_local #endif
1793a3d248dSs.makeev_local
1803a3d248dSs.makeev_local finishedTaskCount.Store(0);
1813a3d248dSs.makeev_local
1823a3d248dSs.makeev_local int32 workersCount = scheduler.GetWorkersCount();
1833a3d248dSs.makeev_local TASK_COUNT_PER_WORKER = workersCount * 4;
1843a3d248dSs.makeev_local int32 taskCount = workersCount * TASK_COUNT_PER_WORKER;
1853a3d248dSs.makeev_local
1863a3d248dSs.makeev_local MT::HardwareFullMemoryBarrier();
1873a3d248dSs.makeev_local
1883a3d248dSs.makeev_local MT::StaticVector<YieldTask, 512> tasks;
1893a3d248dSs.makeev_local for(int32 i = 0; i < taskCount; i++)
1903a3d248dSs.makeev_local {
1913a3d248dSs.makeev_local tasks.PushBack(YieldTask());
1923a3d248dSs.makeev_local }
1933a3d248dSs.makeev_local
19444f7e53cSs.makeev_local for(int32 i = 0; i < workersCount; i++)
19544f7e53cSs.makeev_local {
19644f7e53cSs.makeev_local WorkerThreadState& state = workerStates[i];
19744f7e53cSs.makeev_local state.Reset();
19844f7e53cSs.makeev_local }
19944f7e53cSs.makeev_local
20044f7e53cSs.makeev_local
2013a3d248dSs.makeev_local scheduler.RunAsync(MT::TaskGroup::Default(), tasks.Begin(), (uint32)tasks.Size());
2023a3d248dSs.makeev_local
2033a3d248dSs.makeev_local CHECK(scheduler.WaitGroup(MT::TaskGroup::Default(), 10000));
2043a3d248dSs.makeev_local
2053a3d248dSs.makeev_local for(int32 i = 0; i < workersCount; i++)
2063a3d248dSs.makeev_local {
2073a3d248dSs.makeev_local WorkerThreadState& state = workerStates[i];
2083a3d248dSs.makeev_local
2093a3d248dSs.makeev_local CHECK_EQUAL(TASK_COUNT_PER_WORKER, state.counterPhase0);
2103a3d248dSs.makeev_local CHECK_EQUAL(TASK_COUNT_PER_WORKER, state.counterPhase1);
2113a3d248dSs.makeev_local }
2123a3d248dSs.makeev_local
2133a3d248dSs.makeev_local CHECK_EQUAL(taskCount, (int32)finishedTaskCount.Load());
2143a3d248dSs.makeev_local
2153a3d248dSs.makeev_local printf("Yield test: %d tasks finished, used %d workers\n", taskCount, workersCount);
2163a3d248dSs.makeev_local
2173a3d248dSs.makeev_local }
2183a3d248dSs.makeev_local
2193a3d248dSs.makeev_local
2203a3d248dSs.makeev_local
2212f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2222f083884Ss.makeev_local }
223