Threads in general

Libags provides a thread wrapper built on top of GLib's threading API. The AgsThread object synchronizes the thread tree by AgsThread::clock() event. It is somekind of parallelism trap.

All threads within tree synchronize to AgsThread:max-precision per second, because all threads shall have the very same time running in parallel. I talk of tic-based parallelism, with a max-precision of 1000 Hz, each thread synchronizes 1000 times within tree. Giving you strong semantics to compute a deterministic result in a multi-threaded fashion.

Since we want to run tasks exclusively without any interference from competing threads. There is a mutex lock involved just after synchronization and then invokes ags_task_launcher_sync_run(). Be aware the conditional lock can be evaluate to true for many threads.

After how many tics the flow is repeated depends on samplerate and buffer size. If you have an AgsThread with max-precision 1000, samplerate of 44100 common for audio CDs and a buffer size of 512 frames, then the delay until its repeated calculates as following:

Example 3.1. Calculating tic delay

	tic_delay = 1000.0 / 44100.0 * 512.0; // 11.609977324263039
      

As you might have pre-/post-synchronization needing 3 tics to do its work you get 8 unused tics.

Pre-synchronization is used for reading from soundcard or MIDI device. The intermediate tic does the actual audio processing. Post-synchronization is used by outputing to soundcard or exporting to audio file.

Within thread tree context you have to take care not to hang it up with a dead-lock. Usually you have to use the :start_queue to start threads. Alternatively you may want to use void ags_thread_start(AgsThread*). Use :start_cond, which is protect it with :start_mutex, to notify about running thread.

The following example creates a thread and does add an other thread to :start_queue. This causes it to be started as well. Note you want to access :start_queue using :start_mutex to avoid data races. But there is a convience function which does it for you.

Example 3.2. Starting threads

#include <glib.h>
#include <glib-object.h>

#include <ags/libags.h>

AgsThread *main_loop;
AgsThread *thread;

AgsApplicationContext *application_context;

application_context = ags_application_context_get_instance();

main_loop = ags_generic_main_loop_new();
ags_concurrency_provider_set_main_loop(AGS_CONCURRENCY_PROVIDER(application_context),
				       main_loop);

ags_thread_start(main_loop);

thread = ags_thread_new();
ags_thread_add_child_extended(main_loop,
                              thread,
                              TRUE, TRUE);
ags_thread_add_start_queue(main_loop,
			   thread);

      

There many other functions not covered like mutex wrappers ags_thread_lock() and ags_thread_unlock(). As doing a closer look to the API there are functions to lock different parts of the tree. But all these functions should be carefully used, since you might run into a dead-lock.

To find a specific thread type use ags_thread_find(). You can use ags_thread_self() to retrieve your own running thread in case your using Advanced Gtk+ Sequencer thread wrapper.