The built-in capacity Schedules
enables the agent to schedule tasks for future or periodic execution.
A named task may be created with:
def task(name : String) : AgentTask
The replied task may be used for future execution, or controlling the execution.
Example:
agent A {
uses Schedules
var t : AgentTask
def action {
t = task("abc")
}
}
A task has a name that serves as its identifier. You could change the task name by calling the following function:
def setName(task : AgentTask, name : String)
Example:
agent A {
uses Schedules
var t : AgentTask
on Initialize {
t = task("abc")
}
def action {
this.t.setName("newName")
}
}
For running a task once time, the following function is provided:
def execute(task : AgentTask = null, procedure : (Agent) => void) : AgentTask
Without its optional argument, the function submits the given procedure (a lambda expression as defined in the General Syntax Reference) to an executor provided by the runtime platform. The execution of the procedure will be executed once time as soon as possible. This function replies the agent task for controlling its execution.
With the optional argument, the function behaves in a similar way as previously, except that it accepts an agent task as parameter. This task will attach to the given procedure. The replied task is the same as the task given as parameter.
Example:
agent A {
uses Schedules, Logging
var t1 : AgentTask
var t2 : AgentTask
def action {
t1 = execute [ a : Agent |
info(a)
]
t1 = t2.execute [ a : Agent |
info(a)
]
}
}
For running a task in a given delay, the following function is provided:
def in(task : AgentTask = null, delay : long, procedure : (Agent) => void) : AgentTask
Without its optional argument, the function submits the given procedure (a lambda expression as defined in the General Syntax Reference) to an executor provided by the runtime platform. The execution of the procedure will be delayed during the given number of milliseconds. This function replies the agent task for controlling its execution.
With its optional argument, the function behaves in a similar way as the first, except that it accepts an agent task as parameter. This task will attach to the given procedure. The replied task is the same as the task given as parameter.
Example:
agent A {
uses Schedules, Logging
var t1 : AgentTask
var t2 : AgentTask
def myaction {
t1 = in(1000) [ a : Agent |
info(a)
]
t1 = t2.in(1000) [ a : Agent |
info(a)
]
}
}
For running a task at a specific time, the following function is provided:
def at(task : AgentTask = null, time : long, procedure : (Agent) => void) : AgentTask
Without its optional argument, the function submits the given procedure (a lambda expression as defined in the General Syntax Reference) to an executor provided by the runtime platform. The execution of the procedure will start at the provided time. If the given time is not in the futur, the task is not run. This function replies the agent task for controlling its execution.
With its optional argument, the function behaves in a similar way as the first, except that it accepts an agent task as parameter. This task will attach to the given procedure. The replied task is the same as the task given as parameter.
Example:
agent A {
uses Schedules, Logging
var t1 : AgentTask
var t2 : AgentTask
def myaction {
t1 = at(1000) [ a : Agent |
info(a)
]
t1 = t2.at(1000) [ a : Agent |
info(a)
]
}
}
For running a periodic task with a fixed starting rate, the following function is provided:
def every(task : AgentTask = null, delay : long, procedure : (Agent) => void) : AgentTask
The function without the default parameter submits the given procedure (a lambda expression as defined in the General Syntax Reference) to an executor provided by the runtime platform. The execution of the procedure will be launched periodically with a period of the given number of milliseconds. This function replies the agent task for controlling its execution.
The function with the default parameter behaves in a similar way as the first, except that it accepts an agent task as parameter. This task will attach to the given procedure. The replied task is the same as the task given as parameter.
If the duration of the task is greater to the given period length, then multiple task’s instances will be run in parallel. For example, consider the following code:
every(500) [ sleep(2000) ]
At a given time, four instances of the task are run in parallel (A, B, C, D for example):
t= | 0 | 500 | 1000 | 1500 | 2000 | 2500 | 3000 | 3500 | 4000 |
---|---|---|---|---|---|---|---|---|---|
A | X | X | X | X | |||||
B | X | X | X | X | |||||
C | X | X | X | X | |||||
D | X | X | X | X | |||||
E | X | X | X | X | |||||
F | X | X | X | X |
For running a periodic task with a fixed duration between the runs, the following function is provided:
def atFixedDelay(task : AgentTask = null, delay : long, procedure : (Agent) => void) : AgentTask
Without its optional argument, the function submits the given procedure (a lambda expression as defined in the General Syntax Reference) to an executor provided by the runtime platform. The execution of the procedure will be launched periodically with a duration between the runs of the given number of milliseconds. This function replies the agent task for controlling its execution.
With its optional argument, the function behaves in a similar way as the first, except that it accepts an agent task as parameter. This task will attach to the given procedure. The replied task is the same as the task given as parameter.
The atFixedDelay
function has not the same issue ass the every
function regarding the possibility
to have several runs in parallel.
The atFixedDelay
function ensures that only one run of the procedure will be executed at a given time.
For example, the following code may be illustrated by the table below.
atFixedDelay(500) [ sleep(2000) ]
t= | 0 | 500 | 1000 | 1500 | 2000 | 2500 | 3000 | 3500 | 4000 | 4500 | 5000 | 5500 | 6000 | 6500 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | X | X | X | X | ||||||||||
B | X | X | X | X | ||||||||||
C | X | X | X | X |
It may be useful to cancel a running task, e.g. a periodic task. The Schedules
capacity
provides the following functions for managing the execution cancellation of an agent task:
def cancel(task : AgentTask, mayInterruptIfRunning : boolean = true) : boolean
def isCanceled(task : AgentTask) : boolean
The first function will reply false
if the task has already completed, has already been canceled,
or could not be canceled for some other reason (a failure means replying false).
If successful, and this task has not started when cancel
is called, this task should never
run. If the task has already started, then the mayInterruptIfRunning
parameter determines
whether the thread executing this task should be interrupted in an attempt to stop the task.
The isCanceled
function enables to test if a task was canceled or not.
Example:
agent A {
uses Schedules
var t1 : AgentTask
var t2 : AgentTask
var t3 : AgentTask
def myaction {
t1 = in(1000) [ ]
t2 = in(5000) [ ]
t2 = in(10000) [ ]
if (!t1.isCanceled) {
t1.cancel
}
if (!t2.isCanceled) {
t2.cancel(true)
}
if (!t3.isCanceled) {
t3.cancel(false)
}
}
}
Sometimes, it may be useful to execute a task if a condition is true
or false
. The
AgentTask
type, which is representing an instance of AgentTask
provides
the functions for assosiating a condition, named the guard, to the task:
def getGuard : (Agent) => boolean
def setGuard(condition : (Agent) => boolean)
The first function replies the guard associated to the task, or null
if
there is no associated guard. The second function enables you to change the associated guard.
Additionaly, the AgentTask
type provides utility functions for easier guard association:
def ifTrue(condition : (Agent) => boolean) : AgentTask
def unless(condition : (Agent) => boolean) : AgentTask
The ifTrue
function is equivalent to setGuard
, except that it is replying the current agent task.
The unless
function sets the guard of the task to the negation of the given condition. It replies
the current task.
Caution The ifTrue
and unless
functions should not be used on the result of the scheduling functions. Indeed, if you call these two function on the value replied by execute
for example, the execution platform could have launched the task before the guard is set.
Consider the following code:
execute [ doSomething ].unless [ myVar > 5 ]
The call to execute
is done before the call to unless
. It means that the execution platform could have
already checked if a guard is assosiated and true
, before the unless
function sets the guard.
The best practice for setting the task guards is to create a task, set the guard, and execute the task:
// Create the task instance
var myTask = task(null)
// Set the guard
myTask.unless [ myVar > 5 ]
// Execute the task
myTask.execute [ doSomething ]
The list of the active tasks may be retreived by invoking the following function:
def getActiveTasks : Collection<String>
The replied collection is unmodifiable and contains the names of the active tasks.
Example:
for (taskName : getActiveTasks) {
info("Active task: " + taskName)
}
Copyright © 2014-2024 SARL.io, the Original Authors and Main Authors.
Documentation text and medias are licensed under the Creative Common CC-BY-SA-4.0; you may not use this file except in compliance with CC-BY-SA-4.0. You may obtain a copy of CC-BY-4.0.
Examples of SARL code are licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the Apache License. You may obtain a copy of the Apache License.
You are free to reproduce the content of this page on copyleft websites such as Wikipedia.
Generated with the translator docs.generator 0.14.0.