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>
26*3a3d248dSs.makeev_local #include <MTStaticVector.h>
272f083884Ss.makeev_local 
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();
742f083884Ss.makeev_local 		MT::Thread::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 }
99*3a3d248dSs.makeev_local 
100*3a3d248dSs.makeev_local 
101*3a3d248dSs.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102*3a3d248dSs.makeev_local 
103*3a3d248dSs.makeev_local 
104*3a3d248dSs.makeev_local struct WorkerThreadState
105*3a3d248dSs.makeev_local {
106*3a3d248dSs.makeev_local 	uint32 counterPhase0;
107*3a3d248dSs.makeev_local 	uint32 counterPhase1;
108*3a3d248dSs.makeev_local 
109*3a3d248dSs.makeev_local 	WorkerThreadState()
110*3a3d248dSs.makeev_local 	{
111*3a3d248dSs.makeev_local 		counterPhase0 = 0;
112*3a3d248dSs.makeev_local 		counterPhase1 = 0;
113*3a3d248dSs.makeev_local 	}
114*3a3d248dSs.makeev_local };
115*3a3d248dSs.makeev_local 
116*3a3d248dSs.makeev_local 
117*3a3d248dSs.makeev_local WorkerThreadState workerStates[64];
118*3a3d248dSs.makeev_local 
119*3a3d248dSs.makeev_local uint32 TASK_COUNT_PER_WORKER = 0;
120*3a3d248dSs.makeev_local 
121*3a3d248dSs.makeev_local MT::Atomic32<uint32> finishedTaskCount;
122*3a3d248dSs.makeev_local 
123*3a3d248dSs.makeev_local struct YieldTask
124*3a3d248dSs.makeev_local {
125*3a3d248dSs.makeev_local 	MT::Atomic32<uint32> counter;
126*3a3d248dSs.makeev_local 
127*3a3d248dSs.makeev_local 	MT_DECLARE_TASK(YieldTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue);
128*3a3d248dSs.makeev_local 
129*3a3d248dSs.makeev_local 	YieldTask()
130*3a3d248dSs.makeev_local 	{
131*3a3d248dSs.makeev_local 		counter.Store(0);
132*3a3d248dSs.makeev_local 	}
133*3a3d248dSs.makeev_local 
134*3a3d248dSs.makeev_local 
135*3a3d248dSs.makeev_local 	 volatile WorkerThreadState* GetWorkerState( volatile uint32 workerIndex) volatile
136*3a3d248dSs.makeev_local 	{
137*3a3d248dSs.makeev_local 		MT_ASSERT(workerIndex < MT_ARRAY_SIZE(workerStates), "Invalid worker index");
138*3a3d248dSs.makeev_local 		volatile WorkerThreadState& state = workerStates[workerIndex];
139*3a3d248dSs.makeev_local 		return &state;
140*3a3d248dSs.makeev_local 	}
141*3a3d248dSs.makeev_local 
142*3a3d248dSs.makeev_local 	void Do(MT::FiberContext& context)
143*3a3d248dSs.makeev_local 	{
144*3a3d248dSs.makeev_local 		volatile WorkerThreadState* state0 = GetWorkerState( context.GetThreadContext()->workerIndex );
145*3a3d248dSs.makeev_local 
146*3a3d248dSs.makeev_local 		// phase 0
147*3a3d248dSs.makeev_local 		CHECK_EQUAL((uint32)1, counter.IncFetch());
148*3a3d248dSs.makeev_local 		state0->counterPhase0++;
149*3a3d248dSs.makeev_local 		context.Yield();
150*3a3d248dSs.makeev_local 
151*3a3d248dSs.makeev_local 		// worker index can be changed after yield, get actual index
152*3a3d248dSs.makeev_local 		volatile WorkerThreadState* state1 = GetWorkerState( context.GetThreadContext()->workerIndex );
153*3a3d248dSs.makeev_local 
154*3a3d248dSs.makeev_local 		//I check that all the tasks (on this worker) have passed phase0 before executing phase1
155*3a3d248dSs.makeev_local 		CHECK_EQUAL(TASK_COUNT_PER_WORKER, state1->counterPhase0);
156*3a3d248dSs.makeev_local 
157*3a3d248dSs.makeev_local 		// phase 1
158*3a3d248dSs.makeev_local 		CHECK_EQUAL((uint32)2, counter.IncFetch());
159*3a3d248dSs.makeev_local 		state1->counterPhase1++;
160*3a3d248dSs.makeev_local 
161*3a3d248dSs.makeev_local 		finishedTaskCount.IncFetch();
162*3a3d248dSs.makeev_local 	}
163*3a3d248dSs.makeev_local };
164*3a3d248dSs.makeev_local 
165*3a3d248dSs.makeev_local 
166*3a3d248dSs.makeev_local TEST(YieldTasks)
167*3a3d248dSs.makeev_local {
168*3a3d248dSs.makeev_local 	// Disable task stealing (for testing purposes only)
169*3a3d248dSs.makeev_local #ifdef MT_INSTRUMENTED_BUILD
170*3a3d248dSs.makeev_local 	MT::TaskScheduler scheduler(0, nullptr, nullptr, MT::TaskStealingMode::DISABLED);
171*3a3d248dSs.makeev_local #else
172*3a3d248dSs.makeev_local 	MT::TaskScheduler scheduler(0, nullptr, MT::TaskStealingMode::DISABLED);
173*3a3d248dSs.makeev_local #endif
174*3a3d248dSs.makeev_local 
175*3a3d248dSs.makeev_local 	finishedTaskCount.Store(0);
176*3a3d248dSs.makeev_local 
177*3a3d248dSs.makeev_local 	int32 workersCount = scheduler.GetWorkersCount();
178*3a3d248dSs.makeev_local 	TASK_COUNT_PER_WORKER = workersCount * 4;
179*3a3d248dSs.makeev_local 	int32 taskCount = workersCount * TASK_COUNT_PER_WORKER;
180*3a3d248dSs.makeev_local 
181*3a3d248dSs.makeev_local 	MT::HardwareFullMemoryBarrier();
182*3a3d248dSs.makeev_local 
183*3a3d248dSs.makeev_local 	MT::StaticVector<YieldTask, 512> tasks;
184*3a3d248dSs.makeev_local 	for(int32 i = 0; i < taskCount; i++)
185*3a3d248dSs.makeev_local 	{
186*3a3d248dSs.makeev_local 		tasks.PushBack(YieldTask());
187*3a3d248dSs.makeev_local 	}
188*3a3d248dSs.makeev_local 
189*3a3d248dSs.makeev_local 	scheduler.RunAsync(MT::TaskGroup::Default(), tasks.Begin(), (uint32)tasks.Size());
190*3a3d248dSs.makeev_local 
191*3a3d248dSs.makeev_local 	CHECK(scheduler.WaitGroup(MT::TaskGroup::Default(), 10000));
192*3a3d248dSs.makeev_local 
193*3a3d248dSs.makeev_local 	for(int32 i = 0; i < workersCount; i++)
194*3a3d248dSs.makeev_local 	{
195*3a3d248dSs.makeev_local 		WorkerThreadState& state = workerStates[i];
196*3a3d248dSs.makeev_local 
197*3a3d248dSs.makeev_local 		CHECK_EQUAL(TASK_COUNT_PER_WORKER, state.counterPhase0);
198*3a3d248dSs.makeev_local 		CHECK_EQUAL(TASK_COUNT_PER_WORKER, state.counterPhase1);
199*3a3d248dSs.makeev_local 	}
200*3a3d248dSs.makeev_local 
201*3a3d248dSs.makeev_local 	CHECK_EQUAL(taskCount, (int32)finishedTaskCount.Load());
202*3a3d248dSs.makeev_local 
203*3a3d248dSs.makeev_local 	printf("Yield test: %d tasks finished, used %d workers\n", taskCount, workersCount);
204*3a3d248dSs.makeev_local 
205*3a3d248dSs.makeev_local }
206*3a3d248dSs.makeev_local 
207*3a3d248dSs.makeev_local 
208*3a3d248dSs.makeev_local 
2092f083884Ss.makeev_local ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2102f083884Ss.makeev_local }
211