From a4f3949ab8340938b4f24461560bd0df5fe64540 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Mon, 20 Jul 2020 18:25:00 +0100 Subject: [PATCH] Expanded docs --- docs/basic_usage/action_tasks.rst | 41 ++++++++++++++++++++++++++++ docs/basic_usage/synchronisation.rst | 28 +++++++++++++++++++ src/labthings/tasks/__init__.py | 3 +- src/labthings/tasks/thread.py | 4 ++- 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/docs/basic_usage/action_tasks.rst b/docs/basic_usage/action_tasks.rst index e69de29b..eb2ab150 100644 --- a/docs/basic_usage/action_tasks.rst +++ b/docs/basic_usage/action_tasks.rst @@ -0,0 +1,41 @@ +Action tasks +============ + +Many actions in your LabThing may perform tasks that take a long time (compared to the expected response time of a web request). For example, if you were to implement a timelapse action, this inherently runs over a long time. + +This introduces a couple of problems. Firstly, a request that triggers a long function will, by default, block the Python interpreter for the duration of the function. This usually causes the connection to timeout, and the response will never be revieved. + +Tasks are introduced to manage long-running functions in a way that does not block HTTP requests. Any API Action will automatically run as a task. + +Internally, the :class:`labthings.LabThing` object stores a list of all requested actions, and their states. This state stores the running status of the action (if itis idle, running, error, or success), information about the start and end times, a unique task ID, and, upon completion, the return value of the long-running function. + +By using tasks, a function can be started in the background, and it's return value fetched at a later time once it has reported success. If a long-running action is started by some client, it should note the ID returned in the action state JSON, and use this to periodically check on the status of that particular action. + +API routes have been created to allow checking the state of all actions (GET ``/actions``), a particular task by ID (GET ``/actions/``), and terminating or removing individual tasks (DELETE ``/actions/``). + +All Actions will return a serialized representation of the task, when your POST request returns. If the task completes within a default timeout period (usually 1 second) then the completed Action representation will be returned. If the task is still running after this timeout period, the "in-progress" Action representation will be returned. The final output value can then be retrieved at a later time. + +Most users will not need to create instances of this class. Instead, they will be created automatically when a function is started by an API Action view. + +.. autoclass:: labthings.tasks.TaskThread + :members: + + +Accessing the current task +++++++++++++++++++++++++++ + +A function running inside a :class:`labthings.tasks.TaskThread` is able to access the instance it is running in using the :meth:`labthings.current_task` function. This allows the state of the Action to be modified freely. + +.. autofunction:: labthings.current_task + :noindex: + + +Updating task progress +++++++++++++++++++++++ + +Some client applications may be able to display progress bars showing the progress of an action. Implementing progress updates in your actions is made easy with the :py:meth:`labthings.update_task_progress` function. This function takes a single argument, which is the action progress as an integer percent (0 - 100). + +If your long running function was started within a background task, this function will update the state of the corresponding action object. If your function is called outside of a long-running task (e.g. by some internal code, not the web API), then this function will silently do nothing. + +.. autofunction:: labthings.update_task_progress + :noindex: \ No newline at end of file diff --git a/docs/basic_usage/synchronisation.rst b/docs/basic_usage/synchronisation.rst index e69de29b..539c417a 100644 --- a/docs/basic_usage/synchronisation.rst +++ b/docs/basic_usage/synchronisation.rst @@ -0,0 +1,28 @@ +Synchronisation objects +======================= + + +Locks +----- + +Locks have been implemented to solve a distinct issue, most obvious when considering action tasks. During a long task, it may be necesarry to block any completing interaction with the LabThing hardware. + +The :py:class:`labthings.StrictLock` class is a form of re-entrant lock. Once acquired by a thread, that thread can re-acquire the same lock. This means that other requests or actions will block, or timeout, but the action which acquired the lock is able to re-acquire it. + +.. autoclass:: labthings.StrictLock + :members: + :noindex: + +A CompositeLock allows grouping multiple locks to be simultaneously acquired and released. + +.. autoclass:: labthings.CompositeLock + :members: + :noindex: + + +Per-Client events +----------------- + +.. autoclass:: labthings.ClientEvent + :members: + :noindex: \ No newline at end of file diff --git a/src/labthings/tasks/__init__.py b/src/labthings/tasks/__init__.py index c29b8b74..3aabf348 100644 --- a/src/labthings/tasks/__init__.py +++ b/src/labthings/tasks/__init__.py @@ -7,11 +7,12 @@ ] from .pool import ( + Pool, current_task, update_task_progress, update_task_data, ) -from .thread import TaskKillException +from .thread import TaskThread, TaskKillException # Legacy alias ThreadTerminationError = TaskKillException diff --git a/src/labthings/tasks/thread.py b/src/labthings/tasks/thread.py index 76e5d22f..fa0be5f1 100644 --- a/src/labthings/tasks/thread.py +++ b/src/labthings/tasks/thread.py @@ -16,7 +16,9 @@ class TaskKillException(SystemExit): class TaskThread(threading.Thread): - """ """ + """ + A native thread with extra functionality for tracking progress and thread termination. + """ def __init__(self, target=None, name=None, args=None, kwargs=None, daemon=True): threading.Thread.__init__(