-
Notifications
You must be signed in to change notification settings - Fork 1
POSIX API
The following table lists all STL functions. Where relevant there is a pointer to details of the underlying POSIX function.
MATLAB method | Purpose | POSIX reference |
---|---|---|
stl.argc | Get number of command line arguments | |
stl.argv | Get command line argument | |
stl.log | Print to log | |
stl.launch | Create a thread | pthread_create |
stl.cancel | Cancel a thread | pthread_cancel |
stl.join | Wait for thread to complete | pthread_join |
stl.semaphore | Create semaphore | sem_create |
stl.semaphore_post | Post semaphore | sem_post |
stl.semaphore_wait | Wait for semaphore | sem_wait |
stl.semaphore_try | Test semaphore (non blocking) | sem_wait |
stl.mutex | Create a mutex | pthread_mutex_create |
stl.mutex_lock | Lock a mutex | pthread_mutex_lock |
stl.mutex_try | Lock a mutex (non blocking) | pthread_mutex_lock |
stl.mutex_unlock | Unlock a mutex | pthread_mutex_unlock |
stl.timer | Periodically set semaphore | timer_create |
Each operation is defined by a short method in stl/stl.m
which essentially wraps a function defined by stl.c
which is called using coder.ceval
.
Each additional thread must be defined as a separate MATLAB "entry point function", that is, in a separate m-file.
If the function returns then the thread is terminated. Typically a thread would be a loop, perhaps even an infinite loop. The loop must contain some blocking operation such as waiting for:
- a semaphore or mutex
- a read or write transaction to some device (serial port, I2C etc.)
- a Linux system call.
This is not a cooperative threading environment, threads that do busy/wait polling will be preempted by the scheduler |
---|
Semaphores are used to allow threads to block or enable other threads. For the specific case of protecting a shared resource use a mutex.
id = stl.semaphore(name)
returns the id of a new semaphore, initially not raised.
stl.semaphore_wait(id)
blocks the thread until the semaphore is raised.
v = stl.semaphore_try(id,true)
tests if the semaphore is raised without blocking. If the semaphore is raised it returns true
, otherwise it returns false
.
stl.semaphore.post(id)
the semaphore is raised and one of threads which are waiting on the semaphore is awakened.
Mutexes are a good tool to protecting a resource shared by multiple threads. Only one thread at a time can lock the mutex.
id = stl.mutex(name)
returns the id of a new mutex, initially unlocked.
stl.mutex_lock(id)
blocks the thread until the mutex is unlocked and then this thread locks it.
stl.mutex_try(id,true)
tests if the mutex can be locked, without blocking. If the mutex is unlocked it returns true
and locks the mutex, otherwise it returns false
.
stl.mutex_unlock(id)
the mutex is unlocked and it is available for other threads to lock.
A timer (Linux only) periodically posts a semaphore at a periodic interval. This is useful for periodic tasks where regularity is important (minimize jitter) and where the execution time is variable.
id = stl.timer(name, interval, sem)
creates a new timer that fires every interval
seconds (a double) and posts the semaphore sem
.
This capability is not available for MacOS, see discussion here. A workaround is to use sleep within a thread loop, but this can lead to timing jitter and drift. |
---|
STL comprises a set of MATLAB functions which in turn call functions written in C that interface with the operating system's POSIX libraries.
STL supports a useful subset of POSIX functionality: threads, semaphores and mutexes. Each of these objects is referenced by a small integer handle. The actual POSIX objects/pointers are kept in arrays within stl.c
, the maximum number of threads, semaphores, timers and mutexes (currently 8) can be adjusted by parameters at the top of stl.c
.
All objects have string names (MATLAB character arrays) to assist in debugging, these are shown in the log message stream.
When a resource is freed its STL handle is recycled.
Object | Handle range | Comment |
---|---|---|
thread | 0 | main thread |
thread | 1 → NTHREADS -1 |
user threads |
semaphore | 0 → NSEMAPHORES -1 |
|
mutex | 0 → NMUTEXS -1 |
|
timer | 0 → NTIMERS -1 |
An error is thrown if attempting to allocate, at any one time, more objects than the current maximum.
The C-language main function is provided by stl/main.c
which performs some initialization and then calls user.m
.
% ./user bob alice 123.5
Command line arguments are not passed through to user
but are kept by STL and made available via functions.
stl.argc
returns the number of arguments, always one or more. In the example above it will return 4.
stl.argv(i)
returns the i'th argument as a string. i=0 gives the name of the command, and the maximum value of i is one less than the value returned by stl.argc
. In the example above stl.argv(0) = './user'
which is the command used to invoke the program, and stl.argv(3) = '123.5'
.
Detailed logging of all events to the log channel can be enabled.
stl.debug(true)
Events include object creation, thread launch and exit, semaphore post and wait, mutex lock and unlock. Each entry lists the date and time (to μs precision) and the name of the thread that was executing.
An example log file enabled by debugging is:
2018-08-27 09:25:02.480656 [thread2] hello from thread2, id #2
2018-08-27 09:25:03.479300 [user] cancelling thread #2 <thread2>
2018-08-27 09:25:03.479371 [user] waiting for thread #2 <thread2>
2018-08-27 09:25:03.479435 [user] thread complete #2 <thread2>
2018-08-27 09:25:05.479844 [user] creating semaphore #0 <sem1>
2018-08-27 09:25:05.479977 [user] sem id 0
2018-08-27 09:25:06.481025 [user] thread id 1
2018-08-27 09:25:06.481079 [thread3] starting posix thread <thread3> (0xA162220)
2018-08-27 09:25:06.481109 [thread3] waiting for semaphore #0 <sem1>
Rather than have one M-files for every STL function, they have been written as static methods in the class stl.m
.