[AD] public thread API

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Hi, 

A couple of weeks (months) ago there was a re-suggestion to expose a
thread API.  At that time I started writing down the docs for such a
thing then forgot about it.  Here it is, in source and rendered HTML.

I think I was the only one who had objections to including a thread API,
so whoever feels like working on this should go right ahead.
The internal API mostly matches this already.

* initialisation functions take preallocated objects; should they
  allocate on the heap and return pointers instead?

* I didn't stray from the pthreads word order; should we?

Peter
% Threading API

Allegro 4.9 [is proposed to] include a simple cross-platform threading
interface.  It is a thin layer on top of two threading APIs: Windows threads
and POSIX Threads (pthreads).  Enforcing a consistent semantics on all
platforms would be difficult at best, hence the behaviour of the following
functions will differ subtly on different platforms (more so than usual).
Your best bet is to be aware of this and code to the intersection of the
semantics and avoid edge cases.



-------------------------------------------------------------------------------

[ALLEGRO_THREAD]: #ALLEGRO_THREAD
### ALLEGRO_THREAD

    typedef struct ALLEGRO_THREAD ALLEGRO_THREAD;



[ALLEGRO_MUTEX]: #ALLEGRO_MUTEX
### ALLEGRO_MUTEX

    typedef struct ALLEGRO_MUTEX ALLEGRO_MUTEX;



[ALLEGRO_COND]: #ALLEGRO_COND
### ALLEGRO_COND

    typedef struct ALLEGRO_COND ALLEGRO_COND;



[ALLEGRO_COND_TIMEOUT]: #ALLEGRO_COND_TIMEOUT
### ALLEGRO_COND_TIMEOUT

    typedef struct ALLEGRO_COND_TIMEOUT ALLEGRO_COND_TIMEOUT;

Don't know yet.



[al_thread_create]: #al_thread_create
### al_thread_create

    bool al_thread_create(ALLEGRO_THREAD *thread,
                          void (*proc)(ALLEGRO_THREAD *thread, void *arg),
                          void *arg);

Spawn a new thread which begins executing `proc`.  The new thread is passed
its own thread handle and the value `arg`.

*Return value:*

Returns true if the thread was created, false if there was an error.

*See also:*
[al_thread_join].

*Comment:*
`proc` should return an exit status.



[al_thread_join]: #al_thread_join
### al_thread_join

    void al_thread_join(ALLEGRO_THREAD *thread);

Wait for the thread to finish executing.

*See also:*
[al_thread_should_stop].

*Comment:*
Should return exit status of the proc.



[al_thread_should_stop]: #al_thread_should_stop
### al_thread_should_stop

    bool al_thread_should_stop(ALLEGRO_THREAD *thread);

Check if another thread is waiting for `thread` to stop.  Threads which run
in a loop should check this periodically and act on it when convenient.

*Return value:*

Return true if another thread has called `al_thread_join` on this thread.

*Note:*
We don't support forceful killing of threads.



-------------------------------------------------------------------------------

[al_mutex_init]: #al_mutex_init
### al_mutex_init

    void al_mutex_init(ALLEGRO_MUTEX *mutex);

Initialise the mutex object (a mutual exclusion device).  The mutex may or
may not support "recursive" locking.

*See also:*
[al_mutex_init_recursive].



[al_mutex_init_recursive]: #al_mutex_init_recursive
### al_mutex_init_recursive

    void al_mutex_init_recursive(ALLEGRO_MUTEX *mutex);

Initialise the mutex object (a mutual exclusion device), with support for
"recursive" locking.  That is, the mutex will count the number of times it
has been locked by the same thread.  If the caller tries to acquire a lock
on the mutex when it already holds the lock then the count is incremented.
The mutex is only unlocked when the thread releases the lock on the mutex an
equal number of times, i.e. the count drops down to zero.

*See also:*
[al_mutex_init].



[al_mutex_lock]: #al_mutex_lock
### al_mutex_lock

    void al_mutex_lock(ALLEGRO_MUTEX *mutex);

Acquire the lock on `mutex`.  If the mutex is already locked by another
thread, the call will block until the mutex becomes available and locked.

If the mutex is already locked by the calling thread, then the behaviour
depends on whether the mutex was initialised with `al_mutex_init` or
`al_mutex_init_recursive`.  In the former case, the behaviour is undefined;
the most likely behaviour is deadlock.  In the latter case, the count in the
mutex will be incremented and the call will return immediately.

*See also:*
[al_mutex_unlock].

*Comment:*
We don't yet have al_mutex_trylock.



[al_mutex_unlock]: #al_mutex_unlock
### al_mutex_unlock

    void _al_mutex_unlock(_AL_MUTEX *mutex);

Release the lock on `mutex` if the calling thread holds the lock on it.

If the calling thread doesn't hold the lock, or if the mutex is not locked,
undefined behaviour results.

*See also:*
[al_mutex_lock].



[al_mutex_destroy]: #al_mutex_destroy
### al_mutex_destroy

    void al_mutex_destroy(ALLEGRO_MUTEX *mutex);

Free the resources used by the mutex.  The mutex should be unlocked.
Destroying a locked mutex results in undefined behaviour.



-------------------------------------------------------------------------------

[al_cond_init]: #al_cond_init
### al_cond_init

    void al_cond_init(ALLEGRO_COND *cond);

Initialise a condition variable.

Initialising a condition variable which is already initialised results in
undefined behaviour.



[al_cond_destroy]: #al_cond_destroy
### al_cond_destroy

    void al_cond_destroy(ALLEGRO_COND *cond);

Destroy a condition variable.

Destroying a condition variable which has threads block on it results in
undefined behaviour.



[al_cond_wait]: #al_cond_wait
### al_cond_wait

    void al_cond_wait(ALLEGRO_COND *cond, ALLEGRO_MUTEX *mutex);

On entering this function, `mutex` must be locked by the calling thread.
The function will atomically release `mutex` and block on `cond`.  The
function will return when `cond` is "signalled", acquiring the lock on the
mutex in the process.

Example of proper use:

    al_mutex_lock(mutex);
    while (something_not_true) {
        al_cond_wait(cond, mutex);
    }
    do_something();
    al_mutex_unlock(mutex);

The mutex should be locked before checking the condition, and should be
rechecked `al_cond_wait` returns.  `al_cond_wait` can return for other
reasons than the condition becoming true (e.g. the process was signalled).
If multiple threads are blocked on the condition variable, the condition may
no longer be true by the time the second and later threads are unblocked.
Remember not to unlock the mutex prematurely.

*See also:*
[al_cond_timedwait],
[al_cond_broadcast],
[al_cond_signal].



[al_cond_timedwait]: #al_cond_timedwait
### al_cond_timedwait

    int al_cond_timedwait(ALLEGRO_COND *cond, ALLEGRO_MUTEX *mutex,
                          const ALLEGRO_COND_TIMEOUT timeout);

Like `al_cond_wait` but the call can time out.

*Comment:*
Finish the description.



[al_cond_broadcast]: #al_cond_broadcast
### al_cond_broadcast

    void al_cond_broadcast(ALLEGRO_COND *cond);

Unblock all threads currently waiting on a condition variable.  That is,
broadcast that some condition which those threads were waiting for has
become true.

*See also:*
[al_cond_signal].

*Note:*
The pthreads spec says to lock the mutex associated with `cond` before
signalling for predictable scheduling behaviour.



[al_cond_signal]: #al_cond_signal
### al_cond_signal

    void al_cond_signal(ALLEGRO_COND *cond);

Unblock at least one thread waiting on a condition variable.

Generally you should use `al_cond_broadcast` but `al_cond_signal` may be
more efficient when it's applicable.

*See also:*
[al_cond_broadcast].



-------------------------------------------------------------------------------
<!-- vim: set ts=8 sts=4 sw=4 tw=76 et: -->
Title: Threading API

Threading API

Allegro 4.9 [is proposed to] include a simple cross-platform threading interface. It is a thin layer on top of two threading APIs: Windows threads and POSIX Threads (pthreads). Enforcing a consistent semantics on all platforms would be difficult at best, hence the behaviour of the following functions will differ subtly on different platforms (more so than usual). Your best bet is to be aware of this and code to the intersection of the semantics and avoid edge cases.


ALLEGRO_THREAD

typedef struct ALLEGRO_THREAD ALLEGRO_THREAD;

ALLEGRO_MUTEX

typedef struct ALLEGRO_MUTEX ALLEGRO_MUTEX;

ALLEGRO_COND

typedef struct ALLEGRO_COND ALLEGRO_COND;

ALLEGRO_COND_TIMEOUT

typedef struct ALLEGRO_COND_TIMEOUT ALLEGRO_COND_TIMEOUT;

Don't know yet.

al_thread_create

bool al_thread_create(ALLEGRO_THREAD *thread,
                      void (*proc)(ALLEGRO_THREAD *thread, void *arg),
                      void *arg);

Spawn a new thread which begins executing proc. The new thread is passed its own thread handle and the value arg.

Return value:

Returns true if the thread was created, false if there was an error.

See also: al_thread_join.

Comment: proc should return an exit status.

al_thread_join

void al_thread_join(ALLEGRO_THREAD *thread);

Wait for the thread to finish executing.

See also: al_thread_should_stop.

Comment: Should return exit status of the proc.

al_thread_should_stop

bool al_thread_should_stop(ALLEGRO_THREAD *thread);

Check if another thread is waiting for thread to stop. Threads which run in a loop should check this periodically and act on it when convenient.

Return value:

Return true if another thread has called al_thread_join on this thread.

Note: We don't support forceful killing of threads.


al_mutex_init

void al_mutex_init(ALLEGRO_MUTEX *mutex);

Initialise the mutex object (a mutual exclusion device). The mutex may or may not support "recursive" locking.

See also: al_mutex_init_recursive.

al_mutex_init_recursive

void al_mutex_init_recursive(ALLEGRO_MUTEX *mutex);

Initialise the mutex object (a mutual exclusion device), with support for "recursive" locking. That is, the mutex will count the number of times it has been locked by the same thread. If the caller tries to acquire a lock on the mutex when it already holds the lock then the count is incremented. The mutex is only unlocked when the thread releases the lock on the mutex an equal number of times, i.e. the count drops down to zero.

See also: al_mutex_init.

al_mutex_lock

void al_mutex_lock(ALLEGRO_MUTEX *mutex);

Acquire the lock on mutex. If the mutex is already locked by another thread, the call will block until the mutex becomes available and locked.

If the mutex is already locked by the calling thread, then the behaviour depends on whether the mutex was initialised with al_mutex_init or al_mutex_init_recursive. In the former case, the behaviour is undefined; the most likely behaviour is deadlock. In the latter case, the count in the mutex will be incremented and the call will return immediately.

See also: al_mutex_unlock.

Comment: We don't yet have al_mutex_trylock.

al_mutex_unlock

void _al_mutex_unlock(_AL_MUTEX *mutex);

Release the lock on mutex if the calling thread holds the lock on it.

If the calling thread doesn't hold the lock, or if the mutex is not locked, undefined behaviour results.

See also: al_mutex_lock.

al_mutex_destroy

void al_mutex_destroy(ALLEGRO_MUTEX *mutex);

Free the resources used by the mutex. The mutex should be unlocked. Destroying a locked mutex results in undefined behaviour.


al_cond_init

void al_cond_init(ALLEGRO_COND *cond);

Initialise a condition variable.

Initialising a condition variable which is already initialised results in undefined behaviour.

al_cond_destroy

void al_cond_destroy(ALLEGRO_COND *cond);

Destroy a condition variable.

Destroying a condition variable which has threads block on it results in undefined behaviour.

al_cond_wait

void al_cond_wait(ALLEGRO_COND *cond, ALLEGRO_MUTEX *mutex);

On entering this function, mutex must be locked by the calling thread. The function will atomically release mutex and block on cond. The function will return when cond is "signalled", acquiring the lock on the mutex in the process.

Example of proper use:

al_mutex_lock(mutex);
while (something_not_true) {
    al_cond_wait(cond, mutex);
}
do_something();
al_mutex_unlock(mutex);

The mutex should be locked before checking the condition, and should be rechecked al_cond_wait returns. al_cond_wait can return for other reasons than the condition becoming true (e.g. the process was signalled). If multiple threads are blocked on the condition variable, the condition may no longer be true by the time the second and later threads are unblocked. Remember not to unlock the mutex prematurely.

See also: al_cond_timedwait, al_cond_broadcast, al_cond_signal.

al_cond_timedwait

int al_cond_timedwait(ALLEGRO_COND *cond, ALLEGRO_MUTEX *mutex,
                      const ALLEGRO_COND_TIMEOUT timeout);

Like al_cond_wait but the call can time out.

Comment: Finish the description.

al_cond_broadcast

void al_cond_broadcast(ALLEGRO_COND *cond);

Unblock all threads currently waiting on a condition variable. That is, broadcast that some condition which those threads were waiting for has become true.

See also: al_cond_signal.

Note: The pthreads spec says to lock the mutex associated with cond before signalling for predictable scheduling behaviour.

al_cond_signal

void al_cond_signal(ALLEGRO_COND *cond);

Unblock at least one thread waiting on a condition variable.

Generally you should use al_cond_broadcast but al_cond_signal may be more efficient when it's applicable.

See also: al_cond_broadcast.


body {
    margin: auto;
    padding-right: 1em;
    padding-left: 1em;
    max-width: 44em; 
    border-left: 1px solid black;
    border-right: 1px solid black;
    color: black;
    font-family: Verdana, sans-serif;
    font-size: 100%;
    line-height: 140%;
    color: #333; 
}
pre {
    border: 1px dotted gray;
    background-color: #ececec;
    color: #1111111;
    padding: 0.5em;
}
code {
    font-family: monospace;
}
h1 a, h2 a, h3 a, h4 a, h5 a { 
    text-decoration: none;
    color: #7a5ada; 
}
h1, h2, h3, h4, h5 { font-family: verdana;
                     font-weight: bold;
                     border-bottom: 1px dotted black;
                     color: #7a5ada; }
h1 {
        font-size: 130%;
}

h2 {
        font-size: 110%;
}

h3 {
        font-size: 95%;
}

h4 {
        font-size: 90%;
        font-style: italic;
}

h5 {
        font-size: 90%;
        font-style: italic;
}

h1.title {
        font-size: 200%;
        font-weight: bold;
        padding-top: 0.2em;
        padding-bottom: 0.2em;
        text-align: left;
        border: none;
}

dt code {
        font-weight: bold;
}
dd p {
        margin-top: 0;
}

#footer {
        padding-top: 1em;
        font-size: 70%;
        color: gray;
        text-align: center;
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/