# Executor and Spin Explained

{% hint style="warning" %}
This tutorial is based on ROS2 Humble and the behavior described in this tutorial is subject to change in future releases.
{% endhint %}

## Related Readings

* [rclcpp::Executor class reference](https://docs.ros2.org/beta3/api/rclcpp/classrclcpp_1_1executor_1_1Executor.html)
* [Functional difference between spin, spin\_once, and spin\_until\_future\_complete](https://answers.ros.org/question/296480/functional-difference-between-ros2-spin_some-spin_once-and-spin_until_future/)
* [Github discussion on spin function names](https://github.com/ros2/rclcpp/issues/2311)
* [Concurrency and thread safety in ROS2](https://robotics.stackexchange.com/questions/106026/ros2-multi-nodes-each-on-a-thread-in-same-process)
* [ROS2 document - Callback Groups](https://docs.ros.org/en/humble/How-To-Guides/Using-callback-groups.html)
* [Issue - spin\_until\_future\_complete may block forever if nothing wakes the executor after the future completes](https://github.com/ros2/rclcpp/issues/1916)

## Introduction

In this tutorial, we aim to clarify the difference between spin\_once, spin\_some, spin\_until\_future\_complete, and spin. However, due to limited official documentation, the accuracy of our explanations and interpretations is not guaranteed. We've include code at the end of the tutorial for you to independently verify the behaviors. Please note, this content is specifically tailored to ROS2 Humble; other version may exhibit different behaviors.

## A Few Words on Concurrency

It's highly recommended to read the discussion [ROS2: multi nodes, each on a thread in same process](https://robotics.stackexchange.com/questions/106026/ros2-multi-nodes-each-on-a-thread-in-same-process).

> By **default**, ROS 2 is thread safe:
>
> * Each node's callbacks are called mutually exclusive (i.e. only **one** callback **per node** is called at a time, there is no concurrence within a node),
> * Different nodes' callbacks can be executed concurrently:
>
>   * If they run in different processes, or
>   * If they are in the same process and spun by a MultiThreadedExecutor, or
>   * If they are in the same process and you use multiple threads each running a SingleThreadedExecutor,
>
>   But even in those cases, the active callbacks cannot access another nodes' data, and only one callback per node is being executed, so there are no concurrency issues.

In this tutorial, we will not focus on the callback group.

## Experiment Setup

The experimental system consists of

* A publisher that simultaneously publishes the same messages to two different topics.
* A subscriber that subscribes to these two topics. The callback function is used to simulate the task execution and the execution time can be adjusted via a parameter.
* Parameters that control the start-up time of the publisher and subscriber. If the subscriber activates before the publisher, the the executor queue is empty because no messages are published yet. Conversely, if the publisher starts first, the subscriber may see a non-empty queue.
* Parameters that control the message publication rate and task execution time. An execution time longer than the publication interval leads to buildup of tasks in the executor queue. Conversely, if the execution time is shorter, the queue gets drained.

## Spin Once and Spin Some

`spin_once` and `spin_some` have similar behaviors. Upon invocation, the executor inspects the work queue. If the queue is empty, the function returns immediately. If tasks are present, `spin_once` executes a single task, whereas `spin_some` can handle one or more tasks. It's important to note that although new tasks may arrive during execution, the execution will not address them in the current cycle since task collection occurs only once.

### Experiment 1

In this experiment, we demonstrate the call returns immediately if there is no work in the queue. We will  introduce a delay in starting the publisher. Consequently, when the subscriber is initiated, no messages have been published yet, resulting in an empty work queue.

First, let's launch the system with `spin_once`:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_once_repeat.launch.py publisher_warmup_time:=10 publication_interval:=1 subscriber_warmup_time:=1 task_running_time:=5
```

{% endcode %}

Here is the output of the program:

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [104870]
[INFO] [spin_once_repeat-2]: process started with pid [104872]
[spin_once_repeat-2] [INFO] [1703721523.286740357] [subscriber-main-thread]: ----- start to create the subscriber node
[spin_once_repeat-2] [INFO] [1703721524.292396398] [subscriber-main-thread]: ----- subscriber node is created
[spin_once_repeat-2] [INFO] [1703721524.292595346] [subscriber-main-thread]: ----- start spin_once
[spin_once_repeat-2] [INFO] [1703721524.292609020] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721525.292906999] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721526.293163103] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721527.293488979] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721528.293822772] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721529.294154032] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721530.294414491] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721531.294735447] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703721532.294992828] [subscriber-main-thread]: ----- call spin_once
[publisher-1] [INFO] [1703721533.290849977] [task_publisher]: creating publisher and timer
[spin_once_repeat-2] [INFO] [1703721533.295210474] [subscriber-main-thread]: ----- call spin_once
[publisher-1] [INFO] [1703721534.292651668] [task_publisher]: Publishing: 'task 1'
[spin_once_repeat-2] [INFO] [1703721534.295465766] [subscriber-main-thread]: ----- spin_once ends
[INFO] [spin_once_repeat-2]: process has finished cleanly [pid 104872]
[publisher-1] [INFO] [1703721535.292633903] [task_publisher]: Publishing: 'task 2'
[publisher-1] [INFO] [1703721536.292643748] [task_publisher]: Publishing: 'task 3'
[publisher-1] [INFO] [1703721537.292647627] [task_publisher]: Publishing: 'task 4'
[publisher-1] [INFO] [1703721538.292668753] [task_publisher]: Publishing: 'task 5'
```

The log message confirms that the subscriber is created before the first message is published. Moreover, the `spin_once` call returns immediately.

Let's try `spin_some`:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_some_repeat.launch.py publisher_warmup_time:=10 publication_interval:=1 subscriber_warmup_time:=1 task_running_time:=5
```

{% endcode %}

Similar results are produced:

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [106002]
[INFO] [spin_some_repeat-2]: process started with pid [106004]
[spin_some_repeat-2] [INFO] [1703721861.285776631] [subscriber-main-thread]: ----- start to create the subscriber node
[spin_some_repeat-2] [INFO] [1703721862.291873202] [subscriber-main-thread]: ----- subscriber node is created
[spin_some_repeat-2] [INFO] [1703721862.292095052] [subscriber-main-thread]: ----- start spin_some
[spin_some_repeat-2] [INFO] [1703721862.292110432] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721863.292420633] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721864.292675445] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721865.292924385] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721866.293178492] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721867.293437536] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721868.293725006] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721869.293870219] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703721870.294132843] [subscriber-main-thread]: ----- call spin_some
[publisher-1] [INFO] [1703721871.288962120] [task_publisher]: creating publisher and timer
[spin_some_repeat-2] [INFO] [1703721871.294388290] [subscriber-main-thread]: ----- call spin_some
[publisher-1] [INFO] [1703721872.290757235] [task_publisher]: Publishing: 'task 1'
[spin_some_repeat-2] [INFO] [1703721872.294619692] [subscriber-main-thread]: ----- spin_some ends
[INFO] [spin_some_repeat-2]: process has finished cleanly [pid 106004]
[publisher-1] [INFO] [1703721873.290692259] [task_publisher]: Publishing: 'task 2'
[publisher-1] [INFO] [1703721874.290768198] [task_publisher]: Publishing: 'task 3'

```

### Experiment 2

In this experiment, we demonstrate the difference between `spin_once` and `spin_some`. Specifically, `spin_once` only processes a single task in the queue while `spin_some` can handle multiple tasks. To illustrate this, we start the publisher before the subscribing, ensuring that by the time the subscriber is initiated, messages have already been published to the topics. Additionally, the message publication rate is set to be slower than the rate at which tasks can be competed.

Let's start with `spin_once`:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_once_repeat.launch.py publisher_warmup_time:=1 publication_interval:=3 subscriber_warmup_time:=10 task_running_time:=1
```

{% endcode %}

As we can see in the output below, between two calls of `spin_once`, at most one callback is executed:

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [106255]
[INFO] [spin_once_repeat-2]: process started with pid [106257]
[spin_once_repeat-2] [INFO] [1703722264.902515185] [subscriber-main-thread]: ----- start to create the subscriber node
[publisher-1] [INFO] [1703722265.906442638] [task_publisher]: creating publisher and timer
[publisher-1] [INFO] [1703722268.908613701] [task_publisher]: Publishing: 'task 1'
[publisher-1] [INFO] [1703722271.908610507] [task_publisher]: Publishing: 'task 2'
[spin_once_repeat-2] [INFO] [1703722274.906863820] [subscriber-main-thread]: ----- subscriber node is created
[spin_once_repeat-2] [INFO] [1703722274.907071708] [subscriber-main-thread]: ----- start spin_once
[spin_once_repeat-2] [INFO] [1703722274.907087350] [subscriber-main-thread]: ----- call spin_once
[publisher-1] [INFO] [1703722274.908598651] [task_publisher]: Publishing: 'task 3'
[spin_once_repeat-2] [INFO] [1703722275.907374781] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703722276.907591243] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703722276.907733630] [task_subscriber]: start the task task 3
[spin_once_repeat-2] [INFO] [1703722277.907985576] [task_subscriber]: task is complete task 3
[publisher-1] [INFO] [1703722277.908628763] [task_publisher]: Publishing: 'task 4'
[spin_once_repeat-2] [INFO] [1703722278.908218273] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703722278.908302400] [task_subscriber]: start the task task 3
[spin_once_repeat-2] [INFO] [1703722279.908425731] [task_subscriber]: task is complete task 3
[publisher-1] [INFO] [1703722280.908625705] [task_publisher]: Publishing: 'task 5'
[spin_once_repeat-2] [INFO] [1703722280.908680301] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703722281.908934264] [subscriber-main-thread]: ----- call spin_once
[spin_once_repeat-2] [INFO] [1703722281.909057961] [task_subscriber]: start the task task 4
[spin_once_repeat-2] [INFO] [1703722282.909210972] [task_subscriber]: task is complete task 4
[publisher-1] [INFO] [1703722283.908633067] [task_publisher]: Publishing: 'task 6'

```

Now let's check the behavior of `spin_some`:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_some_repeat.launch.py publisher_warmup_time:=1 publication_interval:=3 subscriber_warmup_time:=10 task_running_time:=1
```

{% endcode %}

Here is the output:

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [106414]
[INFO] [spin_some_repeat-2]: process started with pid [106416]
[spin_some_repeat-2] [INFO] [1703722411.352279678] [subscriber-main-thread]: ----- start to create the subscriber node
[publisher-1] [INFO] [1703722412.355485901] [task_publisher]: creating publisher and timer
[publisher-1] [INFO] [1703722415.357177825] [task_publisher]: Publishing: 'task 1'
[publisher-1] [INFO] [1703722418.357209205] [task_publisher]: Publishing: 'task 2'
[spin_some_repeat-2] [INFO] [1703722421.357183365] [subscriber-main-thread]: ----- subscriber node is created
[publisher-1] [INFO] [1703722421.357189710] [task_publisher]: Publishing: 'task 3'
[spin_some_repeat-2] [INFO] [1703722421.357378437] [subscriber-main-thread]: ----- start spin_some
[spin_some_repeat-2] [INFO] [1703722421.357391962] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703722422.357763617] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703722422.357974239] [task_subscriber]: start the task task 3
[spin_some_repeat-2] [INFO] [1703722423.358207789] [task_subscriber]: task is complete task 3
[spin_some_repeat-2] [INFO] [1703722423.358434326] [task_subscriber]: start the task task 3
[publisher-1] [INFO] [1703722424.357224667] [task_publisher]: Publishing: 'task 4'
[spin_some_repeat-2] [INFO] [1703722424.358609446] [task_subscriber]: task is complete task 3
[spin_some_repeat-2] [INFO] [1703722425.358905006] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703722425.359112045] [task_subscriber]: start the task task 4
[spin_some_repeat-2] [INFO] [1703722426.359259373] [task_subscriber]: task is complete task 4
[spin_some_repeat-2] [INFO] [1703722426.359453598] [task_subscriber]: start the task task 4
```

Let's examine the log more closely. The snippet below shows that between two `spin_some` calls, two task 3 are executed. This is expected since the same message is published to two topics. This shows `spin_some` can execute multiple tasks.

Interestingly, despite using a multi-thread executor, the two tasks are executed sequentially. This behavior suggests that the system operates as though only a single thread is allocated to the node.

We also notice that the interval between the two `spin_some` call is approximately 2 seconds, aligning with the combined execution time of two tasks, where each takes about 1 second. This observation highlights the "blocking" nature of the `spin_some` method.

{% code overflow="wrap" %}

```
[spin_some_repeat-2] [INFO] [1703722422.357763617] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703722422.357974239] [task_subscriber]: start the task task 3
[spin_some_repeat-2] [INFO] [1703722423.358207789] [task_subscriber]: task is complete task 3
[spin_some_repeat-2] [INFO] [1703722423.358434326] [task_subscriber]: start the task task 3
[publisher-1] [INFO] [1703722424.357224667] [task_publisher]: Publishing: 'task 4'
[spin_some_repeat-2] [INFO] [1703722424.358609446] [task_subscriber]: task is complete task 3
[spin_some_repeat-2] [INFO] [1703722425.358905006] [subscriber-main-thread]: ----- call spin_some
```

{% endcode %}

### Experiment 3

This experiment, focused on `spin_some`, demonstrates that the executor collects work only once per `spin_some` call.  We configure tasks to run for 3 seconds and publish messages every 1 second. Given that work arrives faster than it can be processed, we will see accumulation of tasks in the queue.

Launch the system using the following command:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_some_repeat.launch.py publisher_warmup_time:=1 publication_interval:=1 subscriber_warmup_time:=10 task_running_time:=3
```

{% endcode %}

It produces the following outputs: (the key is that there are multiple `call spin_some` messages)

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [107142]
[INFO] [spin_some_repeat-2]: process started with pid [107144]
[spin_some_repeat-2] [INFO] [1703723176.275294226] [subscriber-main-thread]: ----- start to create the subscriber node
[publisher-1] [INFO] [1703723177.279028661] [task_publisher]: creating publisher and timer
[publisher-1] [INFO] [1703723178.280777602] [task_publisher]: Publishing: 'task 1'
[publisher-1] [INFO] [1703723179.280792555] [task_publisher]: Publishing: 'task 2'
[publisher-1] [INFO] [1703723180.280780229] [task_publisher]: Publishing: 'task 3'
[publisher-1] [INFO] [1703723181.280840609] [task_publisher]: Publishing: 'task 4'
[publisher-1] [INFO] [1703723182.280792853] [task_publisher]: Publishing: 'task 5'
[publisher-1] [INFO] [1703723183.280864991] [task_publisher]: Publishing: 'task 6'
[publisher-1] [INFO] [1703723184.280812151] [task_publisher]: Publishing: 'task 7'
[publisher-1] [INFO] [1703723185.280807081] [task_publisher]: Publishing: 'task 8'
[spin_some_repeat-2] [INFO] [1703723186.280558357] [subscriber-main-thread]: ----- subscriber node is created
[spin_some_repeat-2] [INFO] [1703723186.280754425] [subscriber-main-thread]: ----- start spin_some
[spin_some_repeat-2] [INFO] [1703723186.280769249] [subscriber-main-thread]: ----- call spin_some
[publisher-1] [INFO] [1703723186.280767472] [task_publisher]: Publishing: 'task 9'
[publisher-1] [INFO] [1703723187.280827878] [task_publisher]: Publishing: 'task 10'
[spin_some_repeat-2] [INFO] [1703723187.281134662] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703723187.281333990] [task_subscriber]: start the task task 9
[publisher-1] [INFO] [1703723188.280855246] [task_publisher]: Publishing: 'task 11'
[publisher-1] [INFO] [1703723189.280850277] [task_publisher]: Publishing: 'task 12'
[publisher-1] [INFO] [1703723190.280844620] [task_publisher]: Publishing: 'task 13'
[spin_some_repeat-2] [INFO] [1703723190.281558348] [task_subscriber]: task is complete task 9
[spin_some_repeat-2] [INFO] [1703723190.281732216] [task_subscriber]: start the task task 11
[publisher-1] [INFO] [1703723191.280854109] [task_publisher]: Publishing: 'task 14'
[publisher-1] [INFO] [1703723192.280859971] [task_publisher]: Publishing: 'task 15'
[publisher-1] [INFO] [1703723193.280857388] [task_publisher]: Publishing: 'task 16'
[spin_some_repeat-2] [INFO] [1703723193.281838492] [task_subscriber]: task is complete task 11
[publisher-1] [INFO] [1703723194.280880210] [task_publisher]: Publishing: 'task 17'
[spin_some_repeat-2] [INFO] [1703723194.282059193] [subscriber-main-thread]: ----- call spin_some
[spin_some_repeat-2] [INFO] [1703723194.282234788] [task_subscriber]: start the task task 15
```

## Spin Until Future is Complete

In the previous section, we observed that both `spin_once` and `spin_some` methods return either when the queue is empty or after the collected work is done. In particular, experiment 3 shows that `spin_some` returns even when there is more work in the queue. You may wonder how can we keep the executor continuously working on new tasks in the queue?

One approach involves manually wrapping the `spin_once` and `spin_some` methods within a loop. Alternatively, you can use the built-in methods `spin_until_future_complete` or `spin`. The difference between `spin_until_future_complete` and `spin` methods is that the "focus time" is bounded by the future argument in `spin_until_future_complete` whereas the "focus time" is unbounded in `spin`.

### Experiment 4

In this experiment, we run a long-running task in a future and pass it to the `spin_until_future_complete` method. Due to [a pending issue](https://github.com/ros2/rclcpp/issues/1916), additional setup is required to wake up the executor, allowing the executor to exit from the `spin_until_future_complete` method.

To launch the experiment, use the command below:

{% code overflow="wrap" %}

```
ros2 launch bringup spin_until_future_complete.launch.py publisher_warmup_time:=1 publication_interval:=2 subscriber_warmup_time:=5 task_running_time:=1
```

{% endcode %}

The output of the program is presented as follows:

```
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [publisher-1]: process started with pid [112108]
[INFO] [spin_until_future_complete-2]: process started with pid [112110]
[spin_until_future_complete-2] [INFO] [1703727953.315024507] [subscriber-main-thread]: ----- start to create the subscriber node
[publisher-1] [INFO] [1703727954.318600781] [task_publisher]: creating publisher and timer
[publisher-1] [INFO] [1703727956.320320568] [task_publisher]: Publishing: 'task 1'
[spin_until_future_complete-2] [INFO] [1703727958.320232632] [subscriber-main-thread]: ----- subscriber node is created
[publisher-1] [INFO] [1703727958.320275752] [task_publisher]: Publishing: 'task 2'
[spin_until_future_complete-2] [INFO] [1703727958.320647246] [subscriber-main-thread]: ----- start spin_some
[spin_until_future_complete-2] [INFO] [1703727958.320657151] [subscriber-main-thread]: doing some work [0]
[spin_until_future_complete-2] [INFO] [1703727958.320914234] [task_subscriber]: start the task task 2
[spin_until_future_complete-2] [INFO] [1703727959.321089551] [task_subscriber]: task is complete task 2
[spin_until_future_complete-2] [INFO] [1703727959.321303642] [task_subscriber]: start the task task 2
[publisher-1] [INFO] [1703727960.320299603] [task_publisher]: Publishing: 'task 3'
[spin_until_future_complete-2] [INFO] [1703727960.321402142] [task_subscriber]: task is complete task 2
[spin_until_future_complete-2] [INFO] [1703727960.321625114] [task_subscriber]: start the task task 3
[spin_until_future_complete-2] [INFO] [1703727961.321797323] [task_subscriber]: task is complete task 3
[spin_until_future_complete-2] [INFO] [1703727961.321989861] [task_subscriber]: start the task task 3
[publisher-1] [INFO] [1703727962.320294331] [task_publisher]: Publishing: 'task 4'
[spin_until_future_complete-2] [INFO] [1703727962.322159138] [task_subscriber]: task is complete task 3
[spin_until_future_complete-2] [INFO] [1703727962.322412436] [task_subscriber]: start the task task 4
[spin_until_future_complete-2] [INFO] [1703727963.320893505] [subscriber-main-thread]: doing some work [1]
[spin_until_future_complete-2] [INFO] [1703727963.322512534] [task_subscriber]: task is complete task 4
[spin_until_future_complete-2] [INFO] [1703727963.322717466] [task_subscriber]: start the task task 4
[publisher-1] [INFO] [1703727964.320283152] [task_publisher]: Publishing: 'task 5'
[spin_until_future_complete-2] [INFO] [1703727964.322817282] [task_subscriber]: task is complete task 4
[spin_until_future_complete-2] [INFO] [1703727964.323053314] [task_subscriber]: start the task task 5
[spin_until_future_complete-2] [INFO] [1703727965.323226421] [task_subscriber]: task is complete task 5
[spin_until_future_complete-2] [INFO] [1703727965.323427751] [task_subscriber]: start the task task 5
[publisher-1] [INFO] [1703727966.320294448] [task_publisher]: Publishing: 'task 6'
[spin_until_future_complete-2] [INFO] [1703727966.323529903] [task_subscriber]: task is complete task 5
[spin_until_future_complete-2] [INFO] [1703727966.323760117] [task_subscriber]: start the task task 6
[spin_until_future_complete-2] [INFO] [1703727967.323863154] [task_subscriber]: task is complete task 6
[spin_until_future_complete-2] [INFO] [1703727967.324076546] [task_subscriber]: start the task task 6
[publisher-1] [INFO] [1703727968.320294840] [task_publisher]: Publishing: 'task 7'
[spin_until_future_complete-2] [INFO] [1703727968.321037042] [subscriber-main-thread]: doing some work [2]
[spin_until_future_complete-2] [INFO] [1703727968.324188134] [task_subscriber]: task is complete task 6
[spin_until_future_complete-2] [INFO] [1703727968.324408263] [task_subscriber]: start the task task 7
[spin_until_future_complete-2] [INFO] [1703727969.324533568] [task_subscriber]: task is complete task 7
[spin_until_future_complete-2] [INFO] [1703727969.324728485] [task_subscriber]: start the task task 7
[publisher-1] [INFO] [1703727970.320293347] [task_publisher]: Publishing: 'task 8'
[spin_until_future_complete-2] [INFO] [1703727970.324830376] [task_subscriber]: task is complete task 7
[spin_until_future_complete-2] [INFO] [1703727970.325058938] [task_subscriber]: start the task task 8
[spin_until_future_complete-2] [INFO] [1703727971.325161432] [task_subscriber]: task is complete task 8
[spin_until_future_complete-2] [INFO] [1703727971.325351216] [task_subscriber]: start the task task 8
[publisher-1] [INFO] [1703727972.320277252] [task_publisher]: Publishing: 'task 9'
[spin_until_future_complete-2] [INFO] [1703727972.325452600] [task_subscriber]: task is complete task 8
[spin_until_future_complete-2] [INFO] [1703727972.325691728] [task_subscriber]: start the task task 9
[spin_until_future_complete-2] [INFO] [1703727973.321177505] [subscriber-main-thread]: ----- !!! ----- all the side word is done.
[spin_until_future_complete-2] [INFO] [1703727973.325863001] [task_subscriber]: task is complete task 9
[spin_until_future_complete-2] [INFO] [1703727973.326011324] [subscriber-main-thread]: ----- spin_some ends
[INFO] [spin_until_future_complete-2]: process has finished cleanly [pid 112110]
[publisher-1] [INFO] [1703727974.320284937] [task_publisher]: Publishing: 'task 10'
[publisher-1] [INFO] [1703727976.320347662] [task_publisher]: Publishing: 'task 11'
```

The key part in the output is highlighted in the section below:&#x20;

{% code overflow="wrap" %}

```
[spin_until_future_complete-2] [INFO] [1703727972.325452600] [task_subscriber]: task is complete task 8
[spin_until_future_complete-2] [INFO] [1703727972.325691728] [task_subscriber]: start the task task 9
[spin_until_future_complete-2] [INFO] [1703727973.321177505] [subscriber-main-thread]: ----- !!! ----- all the side word is done.
[spin_until_future_complete-2] [INFO] [1703727973.325863001] [task_subscriber]: task is complete task 9
[spin_until_future_complete-2] [INFO] [1703727973.326011324] [subscriber-main-thread]: ----- spin_some ends
```

{% endcode %}

It shows that the `spin_until_future_complete` exits when the work in the future is done.

## Spin

`spin` is the most common one among these variants. According to the code comments, it does work periodically as it becomes available to the executor. It's a blocking call and may block indefinitely. Unlike other method, the `spin` method does not return when the work queue is empty. It always waits for additional tasks.

## Download Code

The code is available for download at this [link](https://learnros2.gumroad.com/l/wncli).

©2023 - 2024 all rights reserved


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.learnros2.com/ros/tutorials/executor-and-spin-explained.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
