Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/task.e | 0 | 1 | 0.00% | 1 | 8 | 12.50% | 7 |
Routine | Executed | Lines | Unexecuted | |
task_delay() | 0 | 7 | 0.00% | 7 |
# | Executed | |
1 | namespace task | |
2 | ||
3 | --**** | |
4 | -- == Multi-tasking | |
5 | -- | |
6 | -- < | |
7 | -- | |
8 | -- === General Notes | |
9 | -- | |
10 | -- For a complete overview of the task system, please see the mini-guide | |
11 | -- [[:Multitasking in Euphoria]]. | |
12 | -- | |
13 | -- === Warning | |
14 | -- | |
15 | -- The task system does not yet function in a shared library. Task routine | |
16 | -- calls that are compiled into a shared library are emitted as a NOP (no | |
17 | -- operation) and will therefore have no effect. | |
18 | -- | |
19 | -- It is planned to allow the task system to function in shared libraries | |
20 | -- in future versions of OpenEuphoria. | |
21 | -- | |
22 | -- === Routines | |
23 | ||
24 | 1 | constant M_SLEEP = 64 |
25 | ||
26 | --** | |
27 | -- Suspends a task for a short period, allowing other tasks to run in the meantime. | |
28 | -- | |
29 | -- Parameters: | |
30 | -- # ##delaytime## : an atom, the duration of the delay in seconds. | |
31 | -- | |
32 | -- Comments: | |
33 | -- | |
34 | -- This procedure is similar to [[:sleep]](), but allows for other tasks to run by yielding on a regular basis. | |
35 | -- Like [[:sleep]](), its argument needs not being an integer. | |
36 | -- | |
37 | -- See Also: | |
38 | -- [[:sleep]] | |
39 | ||
40 | 0 | |
41 | atom t | |
42 | 0 | t = time() |
43 | ||
44 | 0 | while time() - t < delaytime do |
45 | 0 | machine_proc(M_SLEEP, 0.01) |
46 | 0 | task_yield() |
47 | 0 | end while |
48 | 0 | end procedure |
49 | ||
50 | --**** | |
51 | -- Signature: | |
52 | -- | |
53 | -- | |
54 | -- Description: | |
55 | -- Restart the clock used for scheduling real-time tasks. | |
56 | -- | |
57 | -- Comments: | |
58 | -- | |
59 | -- Call this routine, some time after calling task_clock_stop(), when you want scheduling of real-time tasks to continue. | |
60 | -- | |
61 | -- [[:task_clock_stop]]() and ##task_clock_start##() can be used to freeze the scheduling of real-time tasks. | |
62 | -- | |
63 | -- ##task_clock_start##() causes the scheduled times of all real-time tasks to be incremented by | |
64 | -- the amount of time since [[:task_clock_stop]]() was called. This allows a game, | |
65 | -- simulation, or other program to continue smoothly. | |
66 | -- | |
67 | -- Time-shared tasks are not affected. | |
68 | -- | |
69 | -- Example 1: | |
70 | -- | |
71 | -- -- freeze the game while the player answers the phone | |
72 | -- task_clock_stop() | |
73 | -- while get_key() = -1 do | |
74 | -- end while | |
75 | -- task_clock_start() | |
76 | -- | |
77 | -- | |
78 | -- See Also: | |
79 | -- [[:task_clock_stop]], [[:task_schedule]], [[:task_yield]], [[:task_suspend]], | |
80 | -- [[:task_delay]] | |
81 | -- | |
82 | ||
83 | --**** | |
84 | -- Signature: | |
85 | -- | |
86 | -- | |
87 | -- Description: | |
88 | -- Stop the scheduling of real-time tasks. | |
89 | -- | |
90 | -- Comments: | |
91 | -- | |
92 | -- Call ##task_clock_stop##() when you want to take time out from scheduling real-time tasks. | |
93 | -- For instance, you want to temporarily suspend a game or simulation for a period of time. | |
94 | -- | |
95 | -- Scheduling will resume when [[:task_clock_start]]() is called. | |
96 | -- | |
97 | -- Time-shared tasks can continue. The current task can also continue, unless it's a real-time task and it yields. | |
98 | -- | |
99 | -- The [[:time]]() function is not affected by this. | |
100 | -- | |
101 | -- See Also: | |
102 | -- [[:task_clock_start]], [[:task_schedule]], [[:task_yield]], [[:task_suspend]], | |
103 | -- [[:task_delay]] | |
104 | -- | |
105 | ||
106 | --**** | |
107 | -- Signature: | |
108 | -- | |
109 | -- | |
110 | -- Description: | |
111 | -- Create a new task, given a home procedure and the arguments passed to it. | |
112 | -- | |
113 | -- Parameters: | |
114 | -- # ##rid## : an integer, the routine_id of a user-defined Euphoria procedure. | |
115 | -- # ##args## : a sequence, the list of arguments that will be passed to this procedure when the task starts executing. | |
116 | -- | |
117 | -- Returns: | |
118 | -- An **atom**, a task identifier, created by the system. It can be used to identify this task to the other Euphoria multitasking routines. | |
119 | -- | |
120 | -- Errors: | |
121 | -- There must be at most 12 parameters in ##args##. | |
122 | -- | |
123 | -- Comments: | |
124 | -- | |
125 | -- ##task_create##() creates a new task, but does not start it executing. You must call [[:task_schedule]]() for this purpose. | |
126 | -- | |
127 | -- Each task has its own set of private variables and its own call stack. Global and local variables are shared between all tasks. | |
128 | -- | |
129 | -- If a run-time error is detected, the traceback will include information on all tasks, with the offending task listed first. | |
130 | -- | |
131 | -- Many tasks can be created that all run the same procedure, possibly with different parameters. | |
132 | -- | |
133 | -- A task cannot be based on a function, since there would be no way of using the function result. | |
134 | -- | |
135 | -- Each task id is unique. ##task_create##() never returns the same task id as it did before. | |
136 | -- Task id's are integer-valued atoms and can be as large as the largest integer-valued atom (15 digits). | |
137 | -- | |
138 | -- Example 1: | |
139 | -- | |
140 | -- mytask = task_create(routine_id("myproc"), {5, 9, "ABC"}) | |
141 | -- | |
142 | -- | |
143 | -- See Also: | |
144 | -- [[:task_schedule]], [[:task_yield]], [[:task_suspend]], [[:task_self]] | |
145 | -- | |
146 | ||
147 | --**** | |
148 | -- Signature: | |
149 | -- | |
150 | -- | |
151 | -- Description: | |
152 | -- Get a sequence containing the task id's for all active or suspended tasks. | |
153 | -- | |
154 | -- Returns: | |
155 | -- A **sequence**, of atoms, the list of all task that are or may be scheduled. | |
156 | -- | |
157 | -- Comments: | |
158 | -- | |
159 | -- This function lets you find out which tasks currently exist. Tasks that have terminated are not included. | |
160 | -- You can pass a task id to [[:task_status]]() to find out more about a particular task. | |
161 | -- | |
162 | -- Example 1: | |
163 | -- | |
164 | -- sequence tasks | |
165 | -- | |
166 | -- tasks = task_list() | |
167 | -- for i = 1 to length(tasks) do | |
168 | -- if task_status(tasks[i]) > 0 then | |
169 | -- printf(1, "task %d is active\n", tasks[i]) | |
170 | -- end if | |
171 | -- end for | |
172 | -- | |
173 | -- | |
174 | -- See Also: | |
175 | -- [[:task_status]], [[:task_create]], [[:task_schedule]], [[:task_yield]], [[:task_suspend]] | |
176 | -- | |
177 | ||
178 | --**** | |
179 | -- Signature: | |
180 | -- | |
181 | -- | |
182 | -- Description: | |
183 | -- Schedule a task to run using a scheduling parameter. | |
184 | -- | |
185 | -- Parameters: | |
186 | -- # ##task_id## : an atom, the identifier of a task that did not terminate yet. | |
187 | -- # ##schedule## : an object, describing when and how often to run the task. | |
188 | -- | |
189 | -- Comments: | |
190 | -- | |
191 | -- ##task_id## must have been returned by [[:task_create]](). | |
192 | -- | |
193 | -- The task scheduler, which is built-in to the Euphoria run-time system, | |
194 | -- will use ##schedule## | |
195 | -- as a guide when scheduling this task. It may not always be possible to achieve the desired | |
196 | -- number of consecutive runs, or the desired time frame. For instance, a task might take so | |
197 | -- long before yielding control, that another task misses its desired time window. | |
198 | -- | |
199 | -- ##schedule## is being interpreted as follows: | |
200 | -- | |
201 | -- ##schedule## is an integer: | |
202 | -- | |
203 | -- This defines ##task_id## as time shared, and tells the task scheduler how many times it | |
204 | -- should the task in one burst before it considers running other tasks. ##schedule## must be greater than zero then. | |
205 | -- | |
206 | -- Increasing this count will increase the percentage of CPU time given to the selected task, | |
207 | -- while decreasing the percentage given to other time-shared tasks. Use trial and error to find the optimal trade off. | |
208 | -- It will also increase the efficiency of the program, since each actual task switch wastes a bit of time. | |
209 | -- | |
210 | -- ##schedule## is a sequence: | |
211 | -- | |
212 | -- In this case, it must be a pair of positive atoms, the first one not being less than the second one. | |
213 | -- This defines ##task_id## as a real time task. | |
214 | -- The pair states the minimum and maximum times, in seconds, to wait before running the task. | |
215 | -- The pair also sets the time interval for subsequent runs of the task, until the next call to ##task_schedule##() or [[:task_suspend]](). | |
216 | -- | |
217 | -- Real-time tasks have a higher priority. Time-shared tasks are run when no real-time task is ready to execute. | |
218 | -- | |
219 | -- ---- | |
220 | -- | |
221 | -- A task can switch back and forth between real-time and time-shared. It all depends on the last call to ##task_schedule##() for that task. | |
222 | -- The scheduler never runs a real-time task before the start of its time frame (min value in the ##{min, max}## pair), | |
223 | -- and it tries to avoid missing the task's deadline (max value). | |
224 | -- | |
225 | -- For precise timing, you can specify the same value for min and max. However, by specifying a range of times, | |
226 | -- you give the scheduler some flexibility. This allows it to schedule tasks more efficiently, | |
227 | -- and avoid non-productive delays. | |
228 | -- When the scheduler must delay, it calls [[:sleep]](), unless the required delay is very short. | |
229 | -- [[:sleep]]() lets the operating system run other programs. | |
230 | -- | |
231 | -- The min and max values can be fractional. If the min value is smaller than the resolution of the scheduler's clock | |
232 | -- (currently 0.01 seconds on //Windows// or // Unix//) then accurate time scheduling cannot be performed, but the | |
233 | -- scheduler will try to run the task several times in a row to approximate what is desired. | |
234 | -- | |
235 | -- For example, if you ask for a min time of 0.002 seconds, then the scheduler will try to run your task | |
236 | -- .01/.002 = 5 times in a row before waiting for the clock to "click" ahead by .01. | |
237 | -- During the next 0.01 seconds it will run your task (up to) another 5 times etc. provided | |
238 | -- your task can be completed 5 times in one clock period. | |
239 | -- | |
240 | -- At program start-up there is a single task running. Its task id is 0, and initially it's a time-shared task | |
241 | -- allowed 1 run per [[:task_yield]](). No other task can run until task 0 executes a [[:task_yield]](). | |
242 | -- | |
243 | -- If task 0 (top-level) runs off the end of the main file, the whole program terminates, | |
244 | -- regardless of what other tasks may still be active. | |
245 | -- | |
246 | -- If the scheduler finds that no task is active, i.e. no task will ever run again (not even task 0), | |
247 | -- it terminates the program with a 0 exit code, similar to [[:abort]](0). | |
248 | -- | |
249 | -- Example 1: | |
250 | -- | |
251 | -- -- Task t1 will be executed up to 10 times in a row before | |
252 | -- -- other time-shared tasks are given control. If a real-time | |
253 | -- -- task needs control, t1 will lose control to the real-time task. | |
254 | -- task_schedule(t1, 10) | |
255 | -- | |
256 | -- -- Task t2 will be scheduled to run some time between 4 and 5 seconds | |
257 | -- -- from now. Barring any rescheduling of t2, it will continue to | |
258 | -- -- execute every 4 to 5 seconds thereafter. | |
259 | -- task_schedule(t2, {4, 5}) | |
260 | -- | |
261 | -- | |
262 | -- See Also: | |
263 | -- [[:task_create]], [[:task_yield]], [[:task_suspend]] | |
264 | -- | |
265 | ||
266 | --**** | |
267 | -- Signature: | |
268 | -- | |
269 | -- | |
270 | -- Description: | |
271 | -- Return the task id of the current task. | |
272 | -- | |
273 | -- Comments: | |
274 | -- | |
275 | -- This value may be needed, if a task wants to schedule or suspend itself. | |
276 | -- | |
277 | -- Example 1: | |
278 | -- | |
279 | -- -- schedule self | |
280 | -- task_schedule(task_self(), {5.9, 6.0}) | |
281 | -- | |
282 | -- | |
283 | -- See Also: | |
284 | -- [[:task_create]], [[:task_schedule]], [[:task_yield]], [[:task_suspend]] | |
285 | -- | |
286 | ||
287 | --**** | |
288 | -- Signature: | |
289 | -- | |
290 | -- | |
291 | -- Description: | |
292 | -- Return the status of a task. | |
293 | -- | |
294 | -- Parameters: | |
295 | -- # ##task_id## : an atom, the id of the task being queried. | |
296 | -- | |
297 | -- Returns: | |
298 | -- An **integer**, | |
299 | -- * -1 ~-- task does not exist, or terminated | |
300 | -- * 0 ~-- task is suspended | |
301 | -- * 1 ~-- task is active | |
302 | -- | |
303 | -- Comments: | |
304 | -- | |
305 | -- A task might want to know the status of one or more other tasks when deciding whether to proceed with some processing. | |
306 | -- | |
307 | -- Example 1: | |
308 | -- | |
309 | -- integer s | |
310 | -- | |
311 | -- s = task_status(tid) | |
312 | -- if s = 1 then | |
313 | -- puts(1, "ACTIVE\n") | |
314 | -- elsif s = 0 then | |
315 | -- puts(1, "SUSPENDED\n") | |
316 | -- else | |
317 | -- puts(1, "DOESN'T EXIST\n") | |
318 | -- end if | |
319 | -- | |
320 | -- | |
321 | -- See Also: | |
322 | -- [[:task_list]], [[:task_create]], [[:task_schedule]], [[:task_suspend]] | |
323 | -- | |
324 | ||
325 | --**** | |
326 | -- Signature: | |
327 | -- | |
328 | -- | |
329 | -- Description: | |
330 | -- Suspend execution of a task. | |
331 | -- | |
332 | -- Parameters: | |
333 | -- # ##task_id## : an atom, the id of the task to suspend. | |
334 | -- | |
335 | -- Comments: | |
336 | -- | |
337 | -- A suspended task will not be executed again unless there is a call to [[:task_schedule]]() for the task. | |
338 | -- | |
339 | -- ##task_id## is a task id returned from [[:task_create]](). | |
340 | --- | |
341 | -- Any task can suspend any other task. If a task suspends itself, the suspension will start as soon as the task calls [[:task_yield]](). | |
342 | -- | |
343 | -- Suspending a task and never scheduling it again is how to kill a task. There is no | |
344 | -- task_kill() primitives because undead tasks were creating too much trouble and confusion. | |
345 | -- As a general fact, nothing that impacts a running task can be effective as long as the task has not yielded. | |
346 | -- | |
347 | -- Example 1: | |
348 | -- | |
349 | -- -- suspend task 15 | |
350 | -- task_suspend(15) | |
351 | -- | |
352 | -- -- suspend current task | |
353 | -- task_suspend(task_self()) | |
354 | -- | |
355 | -- | |
356 | -- See Also: | |
357 | -- [[:task_create]], [[:task_schedule]], [[:task_self]], [[:task_yield]] | |
358 | -- | |
359 | ||
360 | --**** | |
361 | -- Signature: | |
362 | -- | |
363 | -- | |
364 | -- Description: | |
365 | -- Yield control to the scheduler. The scheduler can then choose another task to run, or | |
366 | -- perhaps let the current task continue running. | |
367 | -- | |
368 | -- Comments: | |
369 | -- | |
370 | -- Tasks should call ##task_yield##() periodically so other tasks will have a chance to run. | |
371 | -- Only when ##task_yield##() is called, is there a way for the scheduler to take back control | |
372 | -- from a task. This is what's known as cooperative multitasking. | |
373 | -- | |
374 | -- A task can have calls to ##task_yield##() in many different places in its code, and at any depth of subroutine call. | |
375 | -- | |
376 | -- The scheduler will use the current scheduling parameter (see [[:task_schedule]]), | |
377 | -- in determining when to return to the current task. | |
378 | -- | |
379 | -- When control returns, execution will continue with the statement that follows ##task_yield##(). | |
380 | -- The call-stack and all private variables will remain as they were when ##task_yield##() was called. | |
381 | -- Global and local variables may have changed, due to the execution of other tasks. | |
382 | -- | |
383 | -- Tasks should try to call ##task_yield##() often enough to avoid causing real-time tasks to miss their time window, | |
384 | -- and to avoid blocking time-shared tasks for an excessive period of time. On the other hand, | |
385 | -- there is a bit of overhead in calling ##task_yield##(), and this overhead is slightly larger | |
386 | -- when an actual switch to a different task takes place. | |
387 | -- A ##task_yield##() where the same task continues executing takes less time. | |
388 | -- | |
389 | -- A task should avoid calling ##task_yield##() when it is in the middle of a delicate operation | |
390 | -- that requires exclusive access to some data. Otherwise a race condition could occur, where | |
391 | -- one task might interfere with an operation being carried out by another task. | |
392 | -- In some cases a task might need to mark some data as "locked" or "unlocked" in order to prevent this possibility. | |
393 | -- With cooperative multitasking, these concurrency issues are much less of a problem than with | |
394 | -- the preemptive multitasking that other languages support. | |
395 | -- | |
396 | -- Example 1: | |
397 | -- | |
398 | -- -- From Language war game. | |
399 | -- -- This small task deducts life support energy from either the | |
400 | -- -- large Euphoria ship or the small shuttle. | |
401 | -- -- It seems to run "forever" in an infinite loop, | |
402 | -- -- but it's actually a real-time task that is called | |
403 | -- -- every 1.7 to 1.8 seconds throughout the game. | |
404 | -- -- It deducts either 3 units or 13 units of life support energy each time. | |
405 | -- | |
406 | -- procedure task_life() | |
407 | -- -- independent task: subtract life support energy | |
408 | -- while TRUE do | |
409 | -- if shuttle then | |
410 | -- p_energy(-3) | |
411 | -- else | |
412 | -- p_energy(-13) | |
413 | -- end if | |
414 | -- task_yield() | |
415 | -- end while | |
416 | -- end procedure | |
417 | -- | |
418 | -- | |
419 | -- See Also: | |
420 | -- [[:task_create]], [[:task_schedule]], [[:task_suspend]] |