COVERAGE SUMMARY
FILE SUMMARY
NameExecutedRoutines%ExecutedLines%Unexecuted
/home/matt/eu/rds/include/std/task.e010.00%1812.50%7
ROUTINE SUMMARY
RoutineExecutedLinesUnexecuted
task_delay()070.00%7
LINE COVERAGE DETAIL
#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
241
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
400
41
atom t
420
t = time()
43
440
while time() - t < delaytime do
450
machine_proc(M_SLEEP, 0.01)
460
task_yield()
470
end while
480
end procedure
49
50
--****
51
-- Signature:
52
-- procedure task_clock_start()
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
-- procedure task_clock_stop()
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
-- function task_create(integer rid, sequence args)
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
-- function task_list()
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
-- procedure task_schedule(atom task_id, object schedule)
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
-- function task_self()
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
-- function task_status(atom task_id)
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
-- procedure task_suspend(atom task_id)
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
-- procedure task_yield()
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]]