Many thanks for the clarification Robin, really appreciate it!
One more thing I realized, isn't the secondary thread effectively blocking the lock on
the main thread because the mutex_lock is placed before the cond call or is this the right
way to do it? Namely, should secondary thead have the following structure:
While loop {
mutex_lock
state cond <--this is where the thread supposedly waits (does this have to be
"while" loop or a simple if will do as is the case in my code?)
do something
mutex_unlock
}
Thread exit
Ico
-----Original Message-----
From: Robin Gareus [mailto:robin@gareus.org]
Sent: Friday, October 01, 2010 9:01 PM
To: Ivica Ico Bukvic
Cc: linux-audio-dev(a)lists.linuxaudio.org
Subject: Re: [LAD] question about multithreaded externals in Pd
Hi Ico,
just quick:
pthread_create() returns after the thread context has been created; but
the actual thread-function is not run directly.
The main function may continue before the actual thread is run.
In your case the pd_cwiid_doConnect() can be called before the
pd_cwiid_pthreadForAudioUnfriendlyOperations() enters the while() loop.
That seems to be a problem - it's too much code to dig though for me at
the moment to tell why this is so - but since usleep() seems to solve
the issue, it probably is. (quick check: does it also crash if you
usleep() after the if (argc==2) {}; just before the return(x); ?
`man 3 pthread_yield` is better that usleep(). it's basically a
usleep(minimum-time-needed-to-run-other-threads).
Another option is to wait after thread-creation in the parent on a
barrier or lock that is released by the child-thread when it starts.
2c,
robin
On 10/02/10 02:36, Ivica Ico Bukvic wrote:
Hi all,
I am wondering if anyone can shed some light on the following
predicament. I am by no means a multi-threading guru so any insight
would be most appreciated.
The following are relevant excerpts from the code of an external. AFAIK
the external initializes mutex and cond and spawns a secondary worker
thread that deals with audio-unfriendly (xrun-causing) write operations
to the wiimote and terminates it when the object is destructed waiting
for the thread to join back and then destroying the mutex.
Now, if I add a bit of usleep right after the thread has been spawned
as
part of the constructor (as included below) the
external seems very
stable (e.g. cutting and pasting it as fast as keyboard allows, or in
other words constructing and destructing instances of it as fast as
possible does not result in a crash). Yet, when one does not use usleep
right after spawning the secondary (worker) thread in the constructor,
the whole thing is very crash-prone, almost as if the spawning of thread
does not go well unless given adequate time to do get things all into
sync, so to say, even though this makes to me no sense as the way I
understand it the constructor does not move ahead until
pthread_create
does not return a value (which in this case I am
not bothering to read).
Curiously, when not using usleep, a crash may occur right at creation
time, at any point while the object exists, and even as late as during
its destruction. Any ideas?
P.S. I am also including the entire file for those interested in trying
it out.
Best wishes,
Ico
Relevant excerpts (in random order and incomplete to allow for
greater
legibility):
//struct defining the object
typedef struct _wiimote
{
t_object x_obj; // standard pd object (must be first in struct)
...
//Creating separate threads for actions known to cause sample
drop-outs
pthread_t unsafe_t;
pthread_mutex_t unsafe_mutex;
pthread_cond_t unsafe_cond;
t_float unsafe;
...
t_float led;
...
} t_wiimote;
//constructor
static void *pd_cwiid_new(t_symbol* s, int argc, t_atom *argv)
{
...
x->led = 0;
// spawn threads for actions known to cause sample drop-outs
threadedFunctionParams rPars;
rPars.wiimote = x;
pthread_mutex_init(&x->unsafe_mutex, NULL);
pthread_cond_init(&x->unsafe_cond, NULL);
pthread_create( &x->unsafe_t, NULL, (void *)
&pd_cwiid_pthreadForAudioUnfriendlyOperations, (void *) &rPars);
//WHY IS THIS NECESSARY? I thought that pthread_create call will
first
finish spawning thread before proceeding
usleep(100); //allow thread to sync (is there a better way to do
this?)
...
}
//destructor
static void pd_cwiid_free(t_wiimote* x)
{
if (x->connected) {
pd_cwiid_doDisconnect(x); //this one has nothing to do
with thread but
rather disconnects the wiimote
}
x->unsafe = -1; //to allow secondary thread to exit the while loop
pthread_mutex_lock(&x->unsafe_mutex);
pthread_cond_signal(&x->unsafe_cond);
pthread_mutex_unlock(&x->unsafe_mutex);
pthread_join(x->unsafe_t, NULL);
pthread_mutex_destroy(&x->unsafe_mutex);
...
}
//worker thread
void pd_cwiid_pthreadForAudioUnfriendlyOperations(void *ptr)
{
threadedFunctionParams *rPars = (threadedFunctionParams*)ptr;
t_wiimote *x = rPars->wiimote;
t_float local_led = 0;
t_float local_rumble = 0;
unsigned char local_rpt_mode = x->rpt_mode;
while(x->unsafe > -1) {
pthread_mutex_lock(&x->unsafe_mutex);
if ((local_led == x->led) && (local_rumble == x->rumble)
&&
> (local_rpt_mode == x->rpt_mode)) {
> pthread_cond_wait(&x->unsafe_cond, &x-
>unsafe_mutex);
> }
>
> if (local_led != x->led) {
> local_led = x->led;
> //do something
> }
> }
> if (local_rumble != x->rumble) {
> local_rumble = x->rumble;
> //do something else
> }
>
> ...
>
> pthread_mutex_unlock(&x->unsafe_mutex);
> }
> pthread_exit(0);
> }
>
> //an example of how the thread is affected by the main thread
> void pd_cwiid_setLED(t_wiimote *x, t_floatarg f)
> {
> if (x->connected) {
> x->led = f;
>
> pthread_mutex_lock(&x->unsafe_mutex);
> pthread_cond_signal(&x->unsafe_cond);
> pthread_mutex_unlock(&x->unsafe_mutex);
> }
> }
>
>
>
>
> _______________________________________________
> Linux-audio-dev mailing list
> Linux-audio-dev(a)lists.linuxaudio.org
>
http://lists.linuxaudio.org/listinfo/linux-audio-dev