Skip to content


The library is threadsafe in a way that any method can safely be called from multiple threads and it won't result in an undefined behavior. For example, if there are two threads each starting a movement on a different device on a single serial port, there will not be any conflict or malformed data on the serial port. Both devices will perform the requested operations.

But the library is not threadsafe in a way that would serialize conflicting operations. For example, two threads attempting to move a single axis will result in the device starting the first move and immediately switching to the second move (upon receiving the second command). The first call will fail with @class MovementInterruptedException. The second move will succeed as expected.

The user is responsible for the synchronization of any higher logic or conflicting operations, especially if they consist of multiple method calls.

Asynchronous operations

The calling order of two asynchronous methods (even from a single thread) in languages like C#, Python, or JavaScript does not guarantee the same order of access to the underlying serial port (or other resources). The library is internally asynchronous and will likely process requests in separate OS threads (despite originating in a single thread). As a result, competing commands will be written to the serial port in an arbitrary order. If you need to ensure the order of operations do so by using await or similar mechanism.

In the following example the device may receive the commands in an arbitrary order.

coroutine1 = axis.generic_command_async("stop")
coroutine2 = axis.generic_command_async("home")

move_coroutine = asyncio.gather(coroutine1, coroutine2)

loop = asyncio.get_event_loop()
var task1 = axis.GenericCommandAsync("stop");
var task2 = axis.GenericCommandAsync("home");

Task.WaitAll(task1, task2);
await Promise.all([
CompletableFuture<Void> future1 = axis.genericCommandAsync("stop");
CompletableFuture<Void> future2 = axis.genericCommandAsync("home");

try {
    CompletableFuture.allOf(future1, future2).get();
} catch (InterruptedException | ExecutionException e) {
    throw new RuntimeException(e);

This property comes from the universal design of the library where we separate threading of front-end languages from the underlying Go DLL.