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 }