1 /*******************************************************************************
2 
3     Extended unit tests for the Scheduler module
4 
5     Copyright:
6         Copyright (c) 2017 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module ocean.task.Scheduler_test;
17 
18 import ocean.task.Scheduler;
19 import ocean.task.Task;
20 import ocean.task.util.Timer;
21 import ocean.core.Test;
22 import ocean.core.Enforce;
23 
24 unittest
25 {
26     // ensure that `theScheduler.exception_handler catches unhandled exceptions
27 
28     static class ThrowingTask1 : Task
29     {
30         // throws straight from `schedule`
31 
32         override public void run ( )
33         {
34             enforce(false, "scheduler");
35         }
36     }
37 
38     static class ThrowingTask2 : Task
39     {
40         // throws from `select_cycle_hook`
41 
42         override public void run ( )
43         {
44             theScheduler.processEvents();
45             enforce(false, "scheduler");
46         }
47     }
48 
49     static class ThrowingTask3 : Task
50     {
51         // throws from inside the epoll
52 
53         override public void run ( )
54         {
55             .wait(1);
56             enforce(false, "epoll");
57         }
58     }
59 
60     SchedulerConfiguration config;
61     config.worker_fiber_limit = 1; // make sure tasks run 1 by 1
62     initScheduler(config);
63 
64     int caught = 0;
65     theScheduler.exception_handler = (Task t, Exception e) {
66         test(e !is null);
67         if (t is null)
68             test!("==")(e.msg, "epoll");
69         else
70             test!("==")(e.msg, "scheduler");
71         caught++;
72     };
73 
74     for (int i = 0; i < 3; ++i)
75     {
76         theScheduler.schedule(new ThrowingTask1);
77         theScheduler.schedule(new ThrowingTask2);
78         theScheduler.schedule(new ThrowingTask3);
79     }
80 
81     theScheduler.eventLoop();
82 
83     test!("==")(caught, 9);
84 }
85 
86 unittest
87 {
88     static class SubTask : Task
89     {
90         int result;
91 
92         override void run ( )
93         {
94             result = 41;
95             .wait(1);
96             result = 42;
97         }
98 
99         override void recycle ( )
100         {
101             result = 43;
102         }
103     }
104 
105     static class MainTask : Task
106     {
107         override void run ( )
108         {
109             // suspend once because `await` safeguards against being run
110             // before the scheduler starts
111             theScheduler.processEvents();
112 
113             // block on result of other tasks:
114             auto r1 = theScheduler.awaitResult(new SubTask);
115             auto r2 = theScheduler.awaitResult(new SubTask);
116             test!("==")(r1, 42);
117             test!("==")(r2, 42);
118         }
119     }
120 
121     initScheduler(SchedulerConfiguration.init);
122     theScheduler.schedule(new MainTask);
123     theScheduler.eventLoop();
124 }
125 
126 class DummyTask : Task
127 {
128     int counter;
129 
130     override void run ( )
131     {
132         ++this.counter;
133 
134         auto stats = theScheduler.getStats();
135         test!("==")(stats.worker_fiber_busy, 0);
136     }
137 }
138 
139 unittest
140 {
141     SchedulerConfiguration config;
142     with (config)
143     {
144         specialized_pools = [
145             PoolDescription(DummyTask.classinfo.name, 10240)
146         ];
147     }
148 
149     initScheduler(config);
150 
151     auto task = new DummyTask;
152     theScheduler.schedule(task);
153     test!("==")(task.counter, 1);
154     test(task.finished());
155 
156     theScheduler.eventLoop();
157 }
158 
159 // https://github.com/sociomantic-tsunami/ocean/issues/498
160 
161 class AwaitedTask1 : Task
162 {
163     int result;
164 
165     override void run ( )
166     {
167         theScheduler.processEvents();
168         this.result = 42;
169     }
170 }
171 
172 class AwaitedTask2 : Task
173 {
174     override void run ( )
175     {
176         // exit immediately
177     }
178 }
179 
180 class MainTask : Task
181 {
182     override void run ( )
183     {
184         auto task1 = new AwaitedTask1;
185         auto task2 = new AwaitedTask2;
186         int result = theScheduler.awaitResult(task1);
187         test!("==")(result, 42);
188         theScheduler.await(task2);
189 
190         auto stats = theScheduler.getStats();
191         test!("==")(stats.worker_fiber_busy, 1);
192 
193         auto stats2_ = theScheduler
194             .getSpecializedPoolStats(AwaitedTask1.classinfo.name);
195         Scheduler.SpecializedPoolStats stats2;
196         test(stats2_.get(stats2));
197         test!("==")(stats2.used_fibers, 0);
198         test!("==")(stats2.total_fibers, 1);
199     }
200 }
201 
202 unittest
203 {
204     SchedulerConfiguration config;
205     with (config)
206     {
207         specialized_pools = [
208             PoolDescription(AwaitedTask1.classinfo.name, 10240),
209             PoolDescription(AwaitedTask2.classinfo.name, 10240)
210         ];
211     }
212 
213     initScheduler(config);
214 
215     auto task = new MainTask;
216     theScheduler.queue(task);
217     theScheduler.eventLoop();
218 }
219 
220 // await on already running task
221 
222 unittest
223 {
224     static class SubTask : Task
225     {
226         bool termination = false;
227 
228         override void run ( )
229         {
230             while (!termination)
231                 .wait(1_000);
232         }
233     }
234 
235     static class MainTask : Task
236     {
237         override void run ( )
238         {
239             auto sub = new SubTask;
240             auto task = Task.getThis();
241             // spawns sub task
242             bool timeout = theScheduler.awaitOrTimeout(sub, 2_000);
243             test(timeout);
244             // waits for sub task a bit more but also timeouts
245             timeout = theScheduler.awaitOrTimeout(sub, 2_000);
246             test(timeout);
247             // sets sub task to terminate and awaits unconditionally
248             sub.termination = true;
249             theScheduler.await(sub);
250         }
251     }
252 
253     initScheduler(SchedulerConfiguration.init);
254     theScheduler.schedule(new MainTask);
255     theScheduler.eventLoop();
256 }