[CM] Question about threading and s7

Iain Duncan iainduncanlists at gmail.com
Fri Sep 25 08:18:08 PDT 2020


Thanks Christos, that is what I'm leaning to as well right now. I did some
more work yesterday, and figured out that it is not hard for the Max object
to check the thread it's running in and do a promotion or deferral, so I'm
almost at the point where I can allow the user to mark and instance as
always-high or always-low. That seems like the "safe" way, and then I may
also be able to let the user run with scissors if they want to allow ops in
any thread and manage their shared memory manually by wrapping Max's
critical_region functions (cross-platform mutexes as far as I can tell).

I'd love to hear any other stories from the trenches on how people have
handled this though!

On Fri, Sep 25, 2020 at 3:52 AM Christos Vagias <chris.actondev at gmail.com>
wrote:

> Hi Iain,
>
> Interesting problem and I'd be interested to see any input from others.
> I had the same problem in my VST3 scenario: the gui and the dsp run on
> separate threads!
>
> How I tackled this was with 2 separate s7 instances and they exchange
> messages.
> The messages are stored in the c++ application instance in an
> std::queue<std::string>
>
> The dsp instance has the following c function bound to emit messages
>
> // the emit accepts a list with symbols,numbers etc.
> // the car could be your message type and then the data would follow
> s7_pointer s7vst::dsp_emit(s7_scheme* sc, s7_pointer args) {
>     // the s7 instance holds in *app* the pointer of the relevant
> application object
>     s7vst* that = (s7vst*) s7_c_pointer(s7_name_to_value(sc, "*app*"));
>
>     s7_pointer msg = s7_car(args);
>     std::string msg_str = s7_format(sc, s7_list(sc, 3,
>                                     s7_f(sc),
>                                     s7_make_string(sc, "~A\n"),
>                                     msg
>                                                ));
>     std::unique_lock lock(that->dsp_msg_mutex);
>     that->dsp_messages.push(msg_str);
>     return s7_nil(sc);
> }
>
>
> And then, the gui instance has a "recv" function and acts upon any
> received messages
>
> s7_pointer s7vst::gui_recv(s7_scheme* sc, s7_pointer args) {
>     s7vst* that = (s7vst*) s7_c_pointer(s7_name_to_value(sc, "*app*"));
>     while (!that->dsp_msg_mutex.try_lock()) {
>         cerr << "Gui recv: could not lock!";
>         std::this_thread::sleep_for(std::chrono::milliseconds(10));
>     }
>
>     if (that->dsp_messages.empty()) {
>         that->dsp_msg_mutex.unlock();
>         return s7_nil(sc);
>     }
>     s7_pointer msg_vector = s7_make_vector(sc, that->dsp_messages.size());
>     size_t pos = 0;
>     while (!that->dsp_messages.empty()) {
>         std::string msg = that->dsp_messages.front();
>         that->dsp_messages.pop();
>         s7_pointer port = s7_open_input_string(sc, msg.c_str());
>         s7_pointer obj = s7_read(sc, port);
>         s7_close_input_port(sc, port);
>         s7_vector_set(sc, msg_vector, pos, obj);
>         pos++;
>     }
>
>     that->dsp_msg_mutex.unlock();
>
>     return msg_vector;
> }
>
> At least that's a quick & dirty way that I got it working.
> Sorry for the long snippets.
>
> Hope it's of help
>
>
> On Fri, 25 Sep 2020 at 03:41, Iain Duncan <iainduncanlists at gmail.com>
> wrote:
>
>> Another option I'm thinking of is giving the users a way to expressly
>> send messages to one or the other thread within the object (ie by
>> requesting promote or defer) and then letting them protect the data
>> sensibly themselves. I'm not sure how this is normally done in Scheme
>> though.
>>
>> iain
>>
>> On Thu, Sep 24, 2020 at 6:34 PM Iain Duncan <iainduncanlists at gmail.com>
>> wrote:
>>
>>> Hi folks, I'm hoping someone can help me out with a question around S7.
>>>
>>> In Max/MSP, when setup for live use there are (generally) two threads of
>>> operation, with the high-priority/dsp thread able to interrupt the low/GUI.
>>> If one doesn't do anything special, this means a max external (such as my
>>> Scheme-for-Max) could be receiving messages in both: low for things
>>> originating from a GUI action, high from metronomes or midi input.  I
>>> assume that I should not expect all to be ok if I have one instance of an
>>> s7 interpreter, which could be accessed from either thread, and it could
>>> get interrupted part way through an eval operation to run another eval in
>>> another thread that may access the same data. IE there's no magical thread
>>> protection baked into S7 that I don't know about....
>>>
>>> I'm wrestling with how to deal with this correctly. One option is to
>>> allow users to designate an s4m instance as always-high or always-low,
>>> basically saying if you need to mix low and high priority you should treat
>>> it like an actor model and have two interpreters  that message each other
>>> and share data through some non-scheme shared data structure (like max
>>> buffers or tables). This might be ok because there is a way for me to
>>> insure incoming max messages from any thread are either promoted or
>>> demoted.
>>>
>>> I suppose another option is to get into critical sections, but I can't
>>> see how that make sense if we don't want low priority actions to have the
>>> chance of locking out high ones.
>>>
>>> Strangely, I have not had any issues yet. But I presume that just means
>>> I've been lucky. Cycling 74 does not (anymore) allow the javascript object
>>> to work in both threads, and I'm thinking it must have been around thread
>>> stability issues.
>>>
>>> Any thoughts most welcome!
>>> iain
>>>
>> _______________________________________________
>> Cmdist mailing list
>> Cmdist at ccrma.stanford.edu
>> https://cm-mail.stanford.edu/mailman/listinfo/cmdist
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://cm-mail.stanford.edu/pipermail/cmdist/attachments/20200925/6f64145a/attachment.html>


More information about the Cmdist mailing list