[CM] Question about threading and s7

Christos Vagias chris.actondev at gmail.com
Fri Sep 25 03:51:59 PDT 2020


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/0ca994bc/attachment-0001.html>


More information about the Cmdist mailing list