GAP (hpc) - Chapter 1: Tasks

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 10 11 Ind
[Top of Book] [Contents] [Previous Chapter] [Next Chapter]

[MathJax on]

1 Tasks
1.1 Overview
1.2 Running tasks
1.2-1 RunTask
1.2-2 ScheduleTask
1.2-3 DelayTask
1.2-4 RunAsyncTask
1.2-5 ScheduleAsyncTask
1.2-6 MakeTaskAsync
1.2-7 ImmediateTask
1.2-8 ExecuteTask
1.2-9 WaitTask
1.2-10 WaitAnyTask
1.2-11 TaskResult
1.2-12 CullIdleTasks
1.3 Information about tasks
1.3-1 TaskSuccess
1.3-2 TaskError
1.3-3 CurrentTask
1.3-4 RunningTasks
1.3-5 TaskStarted
1.3-6 TaskFinished
1.3-7 TaskIsAsync
1.4 Cancelling tasks
1.4-1 CancelTask
1.4-2 TaskCancellationRequested
1.4-3 OnTaskCancellation
1.4-4 OnTaskCancellationReturn
1.5 Conditions
1.6 Milestones
1.6-1 NewMilestone
1.6-2 ContributeToMilestone
1.6-3 AchieveMilestone
1.6-4 IsMilestoneAchieved

### 1 Tasks

#### 1.1 Overview

Tasks provide mid- to high-level functionality for programmers to describe asynchronous workflows. A task is an asynchronously or synchronously executing job; functions exist to create tasks that are executed concurrently, on demand, or in the current thread; to wait for their completion, check their status, and retrieve any results.

Here is a simple example of sorting a list in the background:

    gap> task := RunTask(x -> SortedList(x), [3,2,1]);;
    gap> WaitTask(task);
    gap> TaskResult(task);
    [ 1, 2, 3 ]

`RunTask` (1.2-1) dispatches a task to run in the background; a task is described by a function and zero or more arguments that are passed to `RunTask` (1.2-1). `WaitTask` (1.2-9) waits for the task to complete; and `TaskResult` returns the result of the task.

`TaskResult` (1.2-11) does an implicit `WaitTask` (1.2-9), so the second line above can actually be omitted:

    gap> task := RunTask(x -> SortedList(x), [3,2,1]);;
    gap> TaskResult(task);
    [ 1, 2, 3 ]

It is simple to run two tasks in parallel. Let's compute the factorial of 10000 by splitting the work between two tasks:

    gap> task1 := RunTask(Product, [1..5000]);;
    gap> task2 := RunTask(Product, [5001..10000]);;
    gap> TaskResult(task1) * TaskResult(task2) = Factorial(10000);
    true

You can use `DelayTask` (1.2-3) to delay executing the task until its result is actually needed.

    gap> task1 := DelayTask(Product, [1..5000]);;
    gap> task2 := DelayTask(Product, [5001..10000]);;
    gap> WaitTask(task1, task2);
    gap> TaskResult(task1) * TaskResult(task2) = Factorial(10000);
    true

Note that `WaitTask` (1.2-9) is used here to start execution of both tasks; otherwise, `task2` would not be started until `TaskResult(task1)` has been evaluated.

To start execution of a delayed task, you can also use `ExecuteTask`. This has no effect if a task has already been running.

For convenience, you can also use `ImmediateTask` (1.2-7) to execute a task synchronously (i.e., the task is started immediately and the call does not return until the task has completed).

    gap> task := ImmediateTask(x -> SortedList(x), [3,2,1]);;
    gap> TaskResult(task);
    [ 1, 2, 3 ]

This is indistinguishable from calling the function directly, but provides the same interface as normal tasks.

If e.g. you want to call a function only for its side-effects, it can be useful to ignore the result of a task. `RunAsyncTask` (1.2-4) provides the necessary functionality. Such a task cannot be waited for and its result (if any) is ignored.

    gap> RunAsyncTask(function() Print("Hello, world!\n"); end);;
    gap> !list
    --- Thread 0 [0]
    --- Thread 5 [5] (pending output)
    gap> !5
    --- Switching to thread 5
    [5] Hello, world!
    !0
    --- Switching to thread 0
    gap>

For more information on the multi-threaded user interface, see Chapter 4.

Task arguments are generally copied so that both the task that created them and the task that uses them can access the data concurrently without fear of race conditions. To avoid copying, arguments should be made shared or public (see the relevant parts of section 3.6 on migrating objects between regions); shared and public arguments will not be copied.

HPC-GAP currently has multiple implementations of the task API. To use an alternative implementation to the one documented here, set the environment variable `GAP_WORKSTEALING` to a non-empty value before starting GAP.

#### 1.2 Running tasks

##### 1.2-1 RunTask

`‣ RunTask`( func[, arg1, ..., argn] ) | ( function )

`RunTask` prepares a task for execution and starts it. The task will call the function func with arguments arg1 through argn (if provided). The return value of func is the result of the task. The `RunTask` call itself returns a task object that can be used by functions that expect a task argument.

##### 1.2-2 ScheduleTask

`‣ ScheduleTask`( condition, func[, arg1, ..., argn] ) | ( function )

`ScheduleTask` prepares a task for execution, but, unlike `RunTask` (1.2-1) does not start it until condition is met. See on how to construct conditions. Simple examples of conditions are individual tasks, where execution occurs after the task completes, or lists of tasks, where execution occurs after all tasks in the list complete.

    gap> t1 := RunTask(x->x*x, 3);;
    gap> t2 := RunTask(x->x*x, 4);;
    gap> t := ScheduleTask([t1, t2], function()
    >           return TaskResult(t1) + TaskResult(t2);
    >    end);
    gap> TaskResult(t);
    25

While the above example could also be achieved with `RunTask` (1.2-1) in lieu of `ScheduleTask`, since `TaskResult` (1.2-11) would wait for t1 and t2 to complete, the above implementation does not actually start the final task until the others are complete, making it more efficient, since no additional worker thread needs to be occupied.

##### 1.2-3 DelayTask

`‣ DelayTask`( func[, arg1, ..., argn] ) | ( function )

`DelayTask` works as `RunTask` (1.2-1), but its start is delayed until it is being waited for (including implicitly by calling `TaskResult` (1.2-11)).

##### 1.2-4 RunAsyncTask

`‣ RunAsyncTask`( func[, arg1, ..., argn] ) | ( function )

`RunAsyncTask` creates an asynchronous task. It works like `RunTask` (1.2-1), except that its result will be ignored.

##### 1.2-5 ScheduleAsyncTask

`‣ ScheduleAsyncTask`( condition, func[, arg1, ..., argn] ) | ( function )

`ScheduleAsyncTask` creates an asynchronous task. It works like `ScheduleTask` (1.2-2), except that its result will be ignored.

##### 1.2-6 MakeTaskAsync

`‣ MakeTaskAsync`( task ) | ( function )

`MakeTaskAsync` turns a synchronous task into an asynchronous task that cannot be waited for and whose result will be ignored.

##### 1.2-7 ImmediateTask

`‣ ImmediateTask`( func[, arg1, ..., argn] ) | ( function )

`ImmediateTask` executes the task specified by its arguments synchronously, usually within the current thread.

##### 1.2-8 ExecuteTask

`‣ ExecuteTask`( task ) | ( function )

`ExecuteTask` starts task if it is not already running. It has only an effect if its argument is a task returned by `DelayTask` (1.2-3); otherwise, it is a no-op.

##### 1.2-9 WaitTask

`‣ WaitTask`( task1, ..., taskn ) | ( function )

`‣ WaitTask`( condition ) | ( function )

`‣ WaitTasks`( task1, ..., taskn ) | ( function )

`WaitTask` waits until task1 through taskn have completed; after that, it returns. Alternatively, a condition can be passed to `WaitTask` in order to wait until a condition is met. See on how to construct conditions. `WaitTasks` is an alias for `WaitTask`.

##### 1.2-10 WaitAnyTask

`‣ WaitAnyTask`( task1, ..., taskn ) | ( function )

The `WaitAnyTask` function waits for any of its arguments to finish, then returns the number of that task.

    gap> task1 := DelayTask(x->SortedList(x), [3,2,1]);;
    gap> task2 := DelayTask(x->SortedList(x), [6,5,4]);;
    gap> which := WaitAnyTask(task1, task2);
    2
    gap> if which = 1 then
    >      Display(TaskResult(task1));Display(TaskResult(task2));
    >    else
    >      Display(TaskResult(task2));Display(TaskResult(task1));
    >    fi;
    [ 4, 5, 6 ]
    [ 1, 2, 3 ]

One can pass a list of tasks to `WaitAnyTask` as an argument; `WaitAnyTask([task1, ..., taskn])` behaves identically to `WaitAnyTask(task1, ..., taskn)`.

##### 1.2-11 TaskResult

`‣ TaskResult`( task ) | ( function )

The `TaskResult` function returns the result of a task. It implicitly calls `WaitTask` (1.2-9) if that is necessary. Multiple invocations of `TaskResult` with the same task argument will not do repeated waits and always return the same value.

If the function executed by task encounters an error, `TaskResult` returns `fail`. Whether task encountered an error can be checked via `TaskSuccess` (1.3-1). In case of an error, the error message can be retrieved via `TaskError` (1.3-2).

##### 1.2-12 CullIdleTasks

`‣ CullIdleTasks`( ) | ( function )

This function terminates unused worker threads.

#### 1.3 Information about tasks

##### 1.3-1 TaskSuccess

`‣ TaskSuccess`( task ) | ( function )

`TaskSuccess` waits for task and returns `true` if the it finished without encountering an error. Otherwise the function returns `false`.

##### 1.3-2 TaskError

`‣ TaskError`( task ) | ( function )

`TaskError` waits for task and returns its error message, if it encountered an error. If it did not encounter an error, the function returns `fail`.

##### 1.3-3 CurrentTask

`‣ CurrentTask`( ) | ( function )

The `CurrentTask` returns the currently running task.

##### 1.3-4 RunningTasks

`‣ RunningTasks`( ) | ( function )

This function returns the number of currently running tasks. Note that it is only an approximation and can change as new tasks are being started by other threads.

##### 1.3-5 TaskStarted

`‣ TaskStarted`( task ) | ( function )

This function returns true if the task has started executing (i.e., for any non-delayed task), false otherwise.

##### 1.3-6 TaskFinished

`‣ TaskFinished`( task ) | ( function )

This function returns true if the task has finished executing and its result is available, false otherwise.

##### 1.3-7 TaskIsAsync

`‣ TaskIsAsync`( task ) | ( function )

This function returns true if the task is asynchronous, true otherwise.

#### 1.4 Cancelling tasks

HPC-GAP uses a cooperative model for task cancellation. A programmer can request the cancellation of another task, but it is up to that other task to actually terminate itself. The tasks library has functions to request cancellation, to test for the cancellation state of a task, and to perform actions in response to cancellation requests.

##### 1.4-1 CancelTask

`‣ CancelTask`( task ) | ( function )

`CancelTask` submits a request that task is to be cancelled.

##### 1.4-2 TaskCancellationRequested

`‣ TaskCancellationRequested`( task ) | ( function )

`TaskCancellationRequested` returns true if `CancelTask` (1.4-1) has been called for task, false otherwise.

##### 1.4-3 OnTaskCancellation

`‣ OnTaskCancellation`( exit_func ) | ( function )

`OnTaskCancellation` tests if cancellation for the current task has been requested. If so, then exit_func will be called (as a parameterless function) and the current task will be aborted. The result of the current task will be the value of exit_func().

    gap> task := RunTask(function()
    >      while true do
    >        OnTaskCancellation(function() return 314; end);
    >      od;
    >    end);
    gap> CancelTask(task);
    gap> TaskResult(task);
    314

##### 1.4-4 OnTaskCancellationReturn

`‣ OnTaskCancellationReturn`( value ) | ( function )

`OnTaskCancellationReturn` is a convenience function that does the same as:

    OnTaskCancellation(function() return value; end);

#### 1.5 Conditions

`ScheduleTask` (1.2-2) and `WaitTask` (1.2-9) can be made to wait on more complex conditions than just tasks. A condition is either a milestone, a task, or a list of milestones and tasks. `ScheduleTask` (1.2-2) starts its task and `WaitTask` (1.2-9) returns when the condition has been met. A condition represented by a task is met when the task has completed. A condition represented by a milestone is met when the milestone has been achieved (see below). A condition represented by a list is met when all conditions in the list have been met.

#### 1.6 Milestones

Milestones are a way to represent abstract conditions to which multiple tasks can contribute.

##### 1.6-1 NewMilestone

`‣ NewMilestone`( [list] ) | ( function )

The `NewMilestone` function creates a new milestone. Its argument is a list of targets, which must be a list of integers and/or strings. If omitted, the list defaults to `[0]`.

##### 1.6-2 ContributeToMilestone

`‣ ContributeToMilestone`( milestone, target ) | ( function )

The `ContributeToMilestone` milestone function contributes the specified target to the milestone. Once all targets have been contributed to a milestone, it has been achieved.

##### 1.6-3 AchieveMilestone

`‣ AchieveMilestone`( milestone ) | ( function )

The `AchieveMilestone` function allows a program to achieve a milestone in a single step without adding individual targets to it. This is most useful in conjunction with the default value for `NewMilestone` (1.6-1), e.g.

    gap> m := NewMilestone();;
    gap> AchieveMilestone(m);

>

##### 1.6-4 IsMilestoneAchieved

`‣ IsMilestoneAchieved`( milestone ) | ( function )

`IsMilestoneAchieved` tests explicitly if a milestone has been achieved. It returns `true` on success, `false` otherwise.

    gap> m := NewMilestone([1,2]);;
    gap> ContributeToMilestone(m, 1);
    gap> IsMilestoneAchieved(m);
    false
    gap> ContributeToMilestone(m, 2);
    gap> IsMilestoneAchieved(m);
    true

[Top of Book] [Contents] [Previous Chapter] [Next Chapter]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 10 11 Ind

generated by GAPDoc2HTML