The state of a method as it is being evaluated is called the execution frame. Each frame has a few unique values associated with it, which may or may not propagate to the next execution frame.
One or more frames executed in a series are called a task. Tasks will originate from either a connection, a heartbeat, or by a fork in another task. There is no restriction on how many frames a task can execute.
It is possible to stop an executing frame, refresh the ticks it has and then continue executing. This is called preempting. When a task is preempted it stops executing and is queued behind other current tasks. When these tasks are finished executing, the preempted task continues, with a refreshed tick count. Further information is given in the following sections:
Frames
The state of a method as it is being evaluated is called the execution frame. Each frame has a few unique values associated with it, including the maximum operations it can execute, the method itself, the current object and the object defining the method being executed. These values are only relevant to the current frame, and may or may not propagate to the next execution frame.
The maximum operations a frame can execute are also referred to as the ticks. If a frame runs out of ticks before it finishes executing, the ~ticks error is thrown by the interpreter.
These values can be determined by calling the following functions, as is appropriate:
| ticks_left() | The ticks remaining in the frame. |
| method() | The current method. |
| this() | The current object. |
| definer() | The object defining the method. |
| sender() | The object which called this method (the last frame's current object). |
| caller() | The object defining the method which called this method (the last frame's definer). |
Tasks
One or more frames executed in a series is called a task. Tasks will originate from either a connection, a heartbeat, or by a fork in another task. There is no restriction on how many frames a task can execute.
Several different functions return information about a task, or are used on a task-wide scale:
| task_id() | Returns unique ID for the current task. |
| task_info() | Returns information on a task. |
| tasks() | Returns a list of all paused or preempted tasks. |
| stack() | Returns full execution frame stack. |
| user() | Returns task user. |
| set_user() | Sets task user. |
| atomic() | Used to turn on/off atomic execution. |
Preempting
When a task is preempted it stops executing and is queued behind other current tasks. When these tasks are finished executing the preempted task continues with a refreshed tick count. Preempting can be caused by calling either pause() or refresh(). pause() will always preempt immediately. refresh() will only preempt if the tick count is nearly exhausted. Furthermore, if executing atomically, refresh() will not preempt, but will immediately refresh the tick count and continue executing.
Tasks may also be suspended indefinitely by using the function suspend(). The function resume() will resume a suspended task. The function cancel() will cancel a suspended task.
For more information, see the preempting example below.
A Preempting Example
An example of suspending a task is when reading an indefinite amount of input from a connection. Input arrives through the .parse() method on an object (see Networking), which we define as follows, assuming that the object has the object variables reading, read, buffer and lines declared on it. reading is used to store a boolean value of true or false, depending on whether we are reading input or not. read is used to store what we have already read from the connection. buffer is used to store incoming information which is not yet parsable by the function buf_to_strings() (i.e., there is no carriage return yet). lines is used to buffer lines converted by buf_to_strings() from the connection which we have not parsed yet. It is important to use an object variable for both buffer and lines, because if the task suspends all information stored in either variable would be suspended along with that instance of the task.
driver method .parse() {
arg incoming;
var line;
lines = buf_to_strings(buffer + incoming));
buffer = lines[listlen(lines)];
lines = delete(lines, listlen(lines));
// now parse the lines
for line in (buf_to_strings(incoming)) {
if (reading) {
if (line == ".")
.done_reading();
else
read += [line];
} else {
.parse_line(line);
}
}
}
We will not define the method .parse_line(), just assume that it parses the line as a command. When an object wishes to start reading from the connection it calls the method .read() on the connection object:
public method .read() {
// reading just has to be true, so lets save space and store the
// task id there as well, instead of storing it in another variable.
reading = task_id();
// make sure this is an empty list
read = [];
// suspend the task, have our return value be the return value
// of suspend (which is whatever resume() sends)
return suspend();
}
When a period is found as the line being read, the connection is done reading (the period is insignificant, and is only used in this example for convenience), and the method .done_reading() is called:
protected method .done_reading() {
var info, task;
// get a local copy of what we read, and the task id
info = read;
task = reading;
// reset the object variables first
read = [];
reading = 0;
// resume the task, send what we read as the return
// value for suspend()
resume(task, info);
}
Now, all that has to happen to read input from a connection is to call the method .read() on the connection object, and the input read is returned from the method call.
It is possible to set a task as atomic. Atomic tasks cannot be preempted or suspended. If an attempt is made to preempt or suspend an atomic task the error ~atomic is thrown. However, calling refresh() while executing atomically will always refresh the current frame's ticks. A task's atomic state is toggled using the function atomic().
Note: be very cautious when using atomic tasks. Atomic tasks can easilly disrupt the regular integrity of an online environment. See the section on Security.