Multithreading
Multithreading is relatively simply in HERCs Prolog implementation.
To start a separate thread simply use crack instead of res.
- [crack ...] - thread splitter.
Use crack to start a new thread.
The parameters passes form the body of the new thread.
If there is no parameters (i.e. [crack]) then no thread will be created.
In a green thread situation (when threads are simulated)
the new thread takes precedence and is execute until any delay or synchronisation instruction is encountered.
The new thread ends by itself.
It is not possible to stop it from the outside.
- [wait ...] or [wait : *t]
Causes the thread to sleep for specified number of milliseconds.
If variable is passed then wait returns the current time in milliseconds.
There is also a pair form, where you can use colon character to separate the variable.
It is used for consistency with variable mechanism.
CONDUCTOR
There is an internal synchronisation mechanism, which resembles an orchestra conductor.
You can think of it as an object, which sends impulses in the form of ticks, beats and bars.
The purpose of conductor is mostly musical.
- [start] - starts the conductor or resumes after [pause].
- [pause] - pauses the conductor until subsequent [start] or permanent [stop].
- [stop] - stops the conductor.
- [reset] - resets the tick, beat and bar counters to zero.
- [division *beats_per_bar] or [division *beats_per_bar *ticks_per_beat]
Sets the division of the conductor.
Also returns the division if variables are passesd.
- [metrum *top *bottom]
Translates directly to [division *top 90/*bottom] and can be used to specify musical time signature.
Also, returns current time signature if variables passed.
- [tempo *beats_per_minute]
Sets the tempo of the conductor in beats per second (depends on division).
Also return the current tempo if variable passed.
- [signal ...]
Bypasses the timing and push the tick clock by specified number of ticks.
In other words, it works regardless of whether the conductor is started, paused or stopped.
Beat and bar counter are adjusted accordingly.
If no parameter specified, then equivalent to [signal 1].
- [signal_beat ...] - similar to the above but deals with beats.
- [signal_bar ...] - similar to the above but deals with bars.
The following three instructions allow to synchronise threads with conductor.
It is important to understand when they fail.
If a thread waits for a tick or beat or bar and the conductor is suddenly stopped (not paused) then the wait instruction fails.
This solution allowes for threads to detect the fact the conductor was stopped and thus take some action (most likely stop the thread).
On the other hand, if the conductor is inactive (stopped) and a thread performs a wait instruction (either tick, beat or bar), then the wait instruction does not fail.
Instead, it continues waiting.
This is a good scenario when you want multiple threads to start execution (perhaps play-back) synchronously at the same time.
- [wt ...]
Waits specified number of ticks.
If no parameter specified then equivalent to [wt 1].
If variable passed, then returns current tick.
- [beat ...]
Similar to the above but waits specified number of beats.
Interestingly, you can specify fractional number.
In such a case, the fractional part will be approximated in ticks.
- [bar ...]
Similar to tick, but wait specified number of bars.
No fractional values allowed.
CONDUCTORS
Apart from global conductor it is possible to create more conductors dynamically.
You can create as many of them as you like.
Once you have a conductor, you can use all the conductor-related operations in a way similar to object oriented programming.
- [conductor c]
The c symbol gets a new conductor attached.
If variable is specified instead of a symbol, then an anonymous symbol will be created.
- [c command ...]
Witht the new c conductor you can use all the typical operations.
For example, [c start] will start it, while [c stop] will stop it.
Also, [c beat 2] will wait for the next two beats.
SEMAPHORES
Semaphores are the most basic mechanisms of synchronising threads.
You can read about the here.
- [semaphore s] or [semaphore s count]
Creates a semaphore with optionally specified initial count.
If variable is specified instead of symbol s then an anonymous symbol is created.
- [s wait] - waits for the semaphore (entering critical section).
- [s signal] - signal the semaphore (leaving critical section).
- [s enter] - go or fail if semaphore closed (conditionall enter into critical section).
- [s] - close the semaphore.
CRITICAL SECTIONS
Critical sections are constructs guarded internally by semaphore.
This is basically a clause with a semaphore, which is entered using the wait method.
You can call critical section just like any other clause.
However, only one thread will be able to enter the critical section at the time.
- [critical_section [symbol : *parameters] : *commands] - creates a critical section.
MONITORS
Similar to critical sections but does not store instructions inside.
Also, you must supply semaphore entry method (wait/enter) in each subsequent call.
- [monitor mon] - creates a monitor for a given symbol.
- [mon wait/enter : *commands] - executes given commands within the monitor.
ADA-style TASKS