hi all,
i was thinking about adding some non-blocking message queue mechanism to pd, to simplify writing externals with their own worker threads that cannot, by design, block the audio thread.
i've found the need for this in almost all of my pd projects that use threads, and the only one that has solved it somewhat properly is wvvw.. adding support to pd would prevent a lot of hassle. it could serve as an 'external design pattern' actually.
this could be implemented in the form of a pd-message queue. even a simple fixed size ringbuffer containing fixed size pd atoms would be a very nice thing to have as a standard part of pd. it doesn't require platform-specific code, and doesn't use dynamic memory allocation for each message.
this can catch most of the communication needed between the main pd thread and a bunch of isolated worker threads, and do it in a way that forces you to not cause drops.
something like:
typedef struct { t_atom *buffer; int length; // single message length in atoms int size; // total number of messages int read; // read atom index int write; // write atom index } t_queue;
t_atom *queue_read(t_queue *q); // returns NULL if queue empty int queue_write(t_queue *q, t_atom *a); // returns error code if full void queue_free(t_queue *q); t_queue *queue_new(int length, int size);
another thing that i've been looking for, is a standard way to hook into the main scheduler loop to add a poll hook. you can do this with clocks, but an explicit hook at the point where the gui is polled for updates would be much cleaner. maybe 2 hooks: one that runs at the dsp tick, and one that runs at gui tick.
so the pattern is: thread->pd: a message queue with poller in pd scheduler pd->thread: another message queue with poller in worker thread
the pollers are application-specific, but some could be included in the pd api, like a poller that sends messages to a certain object, or a receiving symbol. this is not really necessary though.
my apologies if i missed out one some discussion on this subject, but i think this would be trivial to add and very useful.
tom
Hi Schouten, the ring buffer idea is really great and if you don't mind i'll take this over to flext which uses dynamic allocation at the moment. The flext message queue has a worker thread which is sleeping until being triggered by a thread signal whenever a new message needs to be delivered. It then takes the pd global thread lock (sys_lock) and sends the messages to pd. I consider this to be quite clean since message delivery will take little time. I have chosen this way to work with any pd version, but there a sure more efficient ways which probably only Miller can tell....
best greetings, Thomas
----- Original Message ----- From: "Tom Schouten" doelie@zzz.kotnet.org To: pd-dev@iem.at Sent: Friday, September 10, 2004 4:18 AM Subject: [PD-dev] how to not cause audio drops with threads
hi all,
i was thinking about adding some non-blocking message queue mechanism to pd, to simplify writing externals with their own worker threads that cannot, by design, block the audio thread.
i've found the need for this in almost all of my pd projects that use threads, and the only one that has solved it somewhat properly is wvvw.. adding support to pd would prevent a lot of hassle. it could serve as an 'external design pattern' actually.
this could be implemented in the form of a pd-message queue. even a simple fixed size ringbuffer containing fixed size pd atoms would be a very nice thing to have as a standard part of pd. it doesn't require platform-specific code, and doesn't use dynamic memory allocation for each message.
this can catch most of the communication needed between the main pd thread and a bunch of isolated worker threads, and do it in a way that forces you to not cause drops.
something like:
typedef struct { t_atom *buffer; int length; // single message length in atoms int size; // total number of messages int read; // read atom index int write; // write atom index } t_queue;
t_atom *queue_read(t_queue *q); // returns NULL if queue empty int queue_write(t_queue *q, t_atom *a); // returns error code if full void queue_free(t_queue *q); t_queue *queue_new(int length, int size);
another thing that i've been looking for, is a standard way to hook into the main scheduler loop to add a poll hook. you can do this with clocks, but an explicit hook at the point where the gui is polled for updates would be much cleaner. maybe 2 hooks: one that runs at the dsp tick, and one that runs at gui tick.
so the pattern is: thread->pd: a message queue with poller in pd scheduler pd->thread: another message queue with poller in worker thread
the pollers are application-specific, but some could be included in the pd api, like a poller that sends messages to a certain object, or a receiving symbol. this is not really necessary though.
my apologies if i missed out one some discussion on this subject, but i think this would be trivial to add and very useful.
tom
PD-dev mailing list PD-dev@iem.at http://iem.at/cgi-bin/mailman/listinfo/pd-dev
On Fri, Sep 10, 2004 at 10:50:46AM +0200, Thomas Grill wrote:
Hi Grill,
the ring buffer idea is really great and if you don't mind i'll take this over to flext which uses dynamic allocation at the moment.
of course not.
The flext message queue has a worker thread which is sleeping until being triggered by a thread signal whenever a new message needs to be delivered. It then takes the pd global thread lock (sys_lock) and sends the messages to pd. I consider this to be quite clean since message delivery will take little time. I have chosen this way to work with any pd version, but there a sure more efficient ways which probably only Miller can tell....
so you do use locks in the communication protocol between 2 threads?
if so, that's exactly what i'm trying to avoid with this.. in practice it works most of the time for small things, but it's no guarantee to avoid priority inversion. there's interesting stuff about this on the LAD list you know.
but, i was thinking that making some pattern doc + code snippets you can include in your own external code might be enough to somehow standardize this, instead of adding it to pd.
pls keep me informed of changes to flext.
cheers tom
Hi Grill,
Hi Schouten,
the ring buffer idea is really great and if you don't mind i'll take
this
over to flext which uses dynamic allocation at the moment.
of course not.
already happened.... it's in the cvs, file flqueue.cpp
so you do use locks in the communication protocol between 2 threads?
if so, that's exactly what i'm trying to avoid with this.. in practice it works most of the time for small things, but it's no guarantee to avoid priority inversion. there's interesting stuff about this on the LAD list you know.
i'll have to read this, but it seems the new flext message passing code will work without locks by just depending on head and tail indices to the ring buffer (actually there are two ring buffers, one for the atoms passed, and one for the metadata)
best greetings, Thomas
the ring buffer idea is really great and if you don't mind i'll take
this
over to flext which uses dynamic allocation at the moment.
of course not.
already happened.... it's in the cvs, file flqueue.cpp
ok i found it. completely grillified. :)
so you do use locks in the communication protocol between 2 threads?
if so, that's exactly what i'm trying to avoid with this.. in practice it works most of the time for small things, but it's no guarantee to avoid priority inversion. there's interesting stuff about this on the LAD list you know.
i'll have to read this, but it seems the new flext message passing code will work without locks by just depending on head and tail indices to the ring buffer (actually there are two ring buffers, one for the atoms passed, and one for the metadata)
exactly as i meant indeed. there's one thing about 'volatile' and the read and write indices i'm not exactly sure about though..