MultiThreading

Siril has a main thread, either the GTK+ main thread when Siril GUI is run, or the script thread when Siril CLI is run. This thread manages things and redraws the GUI, so it should not be used to process data or do anything that can be blocking. For this reason, another thread, that we call the processing thread is managed (see src/core/processing.c). This thread is special in the sense that it is the only one managed explicitly by Siril. There is a function that can be used to reserve it, to start something in it, to wait for it. This thread is also awaited for by scripts to synchronize commands, so their programming is asynchronous.

Beyond these two main execution threads, there is a thread pool that we use for the actual multi-threaded execution of processing algorithm, like splitting an image in 8 to process it with 8 cores or process 8 images in parallel with 8 cores. For sequence operations, this is transparent, but sometimes slow operations on a single image can be explicitly parallelized using OpenMP directives. For an example of that, see src/filters/mtf.c.

Siril's preferred mode of parallelism in sequence processing is to work on several images at the same time. Since 1.2, for live stacking in particular, when there are not enough images to work on in parallel for the number of available threads, a new mechanism has been introduced to still allow per-image parallelism, with a number of threads assigned to each image's processing. This number is passed to the image_hook function of the generic sequence processing function. A similar mechanism is available in the generic image processing framework, where a maximum number of threads can be passed, however for single image processing many legacy operations still check com.max_threads directly instead of obeying the generic_img_args->max_threads value. (TODO: update the functions that were converted to use generic_image_worker to obey the worker's threads limitation).

Thread safety

Only the main thread is allowed to run GTK+ code.. The processing thread, the parallel processing thread pool or any user-launched thread must not work with GTK+ for the following reasons:

  • GTK+ is not thread safe and only its main thread can work with the GUI

  • Siril is very much used without a GUI, so calling GTK+ in this context will fail in some way

  • It is not the concern of a processing thread to get data from the GUI or to update the GUI.

This also means that:

  • before processing an image, all input parameters must be obtained, from the GUI or from the command arguments (settings can still be accessed).

  • during the processing, selected thread-safe UI functions like logging messages (siril_log_message()), or updating the progress bar (set_progress_bar_data()) can still be used.

  • after processing, generic operations like computing or saving results, freeing the resources should be done in the processing thread, unless needed for the GUI update, and another function dedicated to the GUI update will have to be run in the main thread. In the GTK+ world, these functions are called Idle Functions. They are registered by any thread and they get run by the main thread whenever it's idle. To avoid running them in the CLI version of Siril, the function siril_add_idle() is used, however by using the generic_image_worker() construct you can generally abstract this away using args->idle_function and let the generic_image_worker take care of adding the idle.

Because some operations are still very often used in threads other than the main thread, like indicating progress, they have been made thread-safe, with the use of Idle Functions.