What's new

Is KSP capable of parallel processing?

Dewdman42

Senior Member
Just coming back to this thread... I am wanting to also use a thread-safe wait in multi-script.. Not entirely sure I'm doing this right, or the best way, but this is what I came up with so far for a simple test....

Code:
on init
    tcm.init(100)
end on

taskfunc deferNote(delay, channel, pitch, velocity)
    tcm.wait(delay*1000)
    set_midi(channel, $MIDI_COMMAND_NOTE_ON, pitch, velocity)
end taskfunc

on midi_in
    if(MIDI_COMMAND = MIDI_COMMAND_NOTE_ON)
        ignore_midi
        deferNote(500, $MIDI_CHANNEL, $MIDI_BYTE_1, $MIDI_BYTE_2) {500ms}
    end if
end on

So the couple points I based this on, which could be wrong or maybe there is easier way, I'd love to hear more...
  1. ignore_midi has to be used to disable the incoming midi event, and then the necessary values saved somewhere so that after the wait, a new call to set_midi can schedule it again.

  2. ignore_midi can't be inside a taskfunc

  3. taskfunc is used for thread-safety on the pitch, channel and velocity values that will be needed to set_midi after the wait.

Just wondering if I am doing this the hard way or if there is an easier or better way. I'm brand-new to KSP and sublime too...so please forgive me if I'm missing something obvious.

The question I have is about the thread-safety of the $EVENT_ID when using wait without taskfunc.. for example, if I do this:

Code:
on midi_in
    if($MIDI_COMMAND = $MIDI_COMMAND_NOTE_ON)
        wait(500000)
   end if
end on

First the above doesn't actually wait to send the note. It seems to get sent immediately no matter the wait in the callback. Not sure why, if anyone knows the precise explanation?

So I had to use ignore_midi and then later set_midi. But if I try to do it without taskfunc, I think thread-safety would not be right, for example:

Code:
on midi_in
    if($MIDI_COMMAND = $MIDI_COMMAND_NOTE_ON)
        ignore_midi
        wait(500000)
        set_midi($MIDI_CHANNEL, $MIDI_COMMAND_NOTE_ON, $MIDI_BYTE_1, $MIDI_BYTE_2)
   end if
end on

I think in this above, the various $MIDI_COMMAND, etc...after coming back from wait could easily have been overwritten by other callbacks that happened while waiting. yea?

So anyway, thus the use of the taskfunc to cache the event values to use for reconstructing an event after wait.

Any comments welcome...
 
Last edited:

EvilDragon

KSP Wizard
First the above doesn't actually wait to send the note. It seems to get sent immediately no matter the wait in the callback. Not sure why, if anyone knows the precise explanation?
That's right. You basically didn't modify the original event, you just added wait to it. Which is why you need to ignore, then wait, then send your event.

This particular case is a good one to use taskfuncs for.
 

Dewdman42

Senior Member
Thanks ED, was I right to assume that doing it the following way would NOT be "thread-safe"? (or perhaps more properly stated, "not re-entrant")

Code:
on midi_in
    if($MIDI_COMMAND = $MIDI_COMMAND_NOTE_ON)
        ignore_midi
        wait(500000)
        set_midi($MIDI_CHANNEL, $MIDI_COMMAND_NOTE_ON, $MIDI_BYTE_1, $MIDI_BYTE_2)
   end if
end on
 
Last edited:

EvilDragon

KSP Wizard
Actually that might even be fine... did you try it and see if you get any hung notes? (Obviously your if condition needs to query both note ons and off)
 

Dewdman42

Senior Member
I have tried it, but the nature of re-entrant race conditions is that it will be unpredictable so hard to test for it to know for sure if its ok. I read up a bunch of KSP stuff about how when callbacks call wait, then other callbacks can begin to run, but the other callbacks are, in the default case, sharing the same various variables of the KSP script...so its possible one of the other callbacks (from another note coming in), would overwrite some variable and then when the first one finished waiting it would not have the same state as before the wait.

That all makes sense and its part of why taskfuncs exist..but mostly what there is to read is about Instrument scripts, not so much the multi-script case. I wasn't sure if each callback of each note..would change the value of $MIDI_COMMAND, $MIDI_CHANNEL, etc...and based on everything I read about KSP not being re-entrant in general....I am assuming that would be the case. For it to be re-entrant it would need to have the preserved values of $MIDI_COMMAND, $MIDI_CHANNEL, etc...after coming back from wait, even if other notes triggered callbacks of their own during that time period and overwrote them in the process... am I making sense?
 

EvilDragon

KSP Wizard
Yes but in the above case you're not using any extra variables, you're using the original event variables of the callback. That's why it should work.
 

Dewdman42

Senior Member
That is I guess the open question in my mind, if you don't know for sure.. whether its variables set in the script or variables set by the engine..I don't really see any indication that KSP is keeping track of state for each callback...without SublimeKSP that is... I think I will rather assume its not unless there is some "for sure" information to the contrary. The whole TSM doc goes into great length about this...

Well... maybe I will try to see if I can make some test cases that might test the theory to see what happens.
 

EvilDragon

KSP Wizard
I just tried the above code you posted but delaying both note on and note off by half a second, and playing the same key really fast, and I didn't get any hung notes. So I suppose this stands to reason that, at least when callback's default variables ($MIDI_CHANNEL, $MIDI_COMMAND, $MIDI_BYTE_1, $MIDI_BYTE_2) are concerned, their state is tracked even through wait().

Makes sense because nothing mutated them from the outside (i.e. by trying to set_event_par() on an $EVENT_ID).
 

Dewdman42

Senior Member
If they both have the same wait time I'm not sure that proves it. I will try to put some more thought into it later tonight if I can..

Really I think the best way is to setup some examples where you know for sure there is an incoming event that will occur while a previous event is waiting. Log the values of $MIDI_CHANNEL, $MIDI_COMMAND, etc before and after the wait in all cases and then analyze it.

I read in the TSM guide also how when there isn't a wait, then the default behavior of KSP is to process every event to completion before starting the next one. its only when an event is waiting that another event can be processed in the meantime. I suspect this is not handled internally by threads, but by queues. First event comes in, hits wait, next event in the queue starts to process and either completes or hits wait, etc. The next event is processed only when the previous event either completes the callback or hits wait. A callback that was waiting, then at some point is used as the next event in the input queue to process once it reaches its wait time..etc.

Taking all of that into consideration a test needs to setup a specific case where we know and incoming note will have changed the values of $MIDI_COMMAND, etc. while the first one was waiting, so that when it comes back..we can see if it still has its original values preserved.

I hope you're right actually, that would make things a lot more simple! I just want to be sure before I do some time oriented KSP multi-scripts.
 

Dewdman42

Senior Member
But I guess, regardless of whether $MIDI_COMMAND, etc are preserved when re-queing an event callback that was waiting.....care still has to go into all the other variables in the program...with any program complexity, it probably still comes down to using taskfuncs to be sure complete state is preserved for each event callback during wait.
 

Dewdman42

Senior Member
So I did my own test like this:

Code:
on init
    declare $mon
    watch_var($mon)
end on

on midi_in

    $mon := $MIDI_BYTE_1
    wait(1000000)
    $mon := $MIDI_BYTE_1

end on

I watched, the variable logging in creatortools as I played different key sequences on the midi keyboard...seems to work fine, as it did in your test ED...so yea..it seems that somehow the various $MIDI_xxx variables are protected in a re-entrant way when each of the callbacks is re-queued after wait, it brings those values back into the script again.

I wonder what other system variables like that are also safe that way? I'm guessing probably all the ones listed near the end of the manual in ALL CAPS.

In any case then, the code I posted earlier is basically unnecessary...though..if any user-declared variables need to be used after wait...then it should be protected in a taskfunc....like you said already...
 

EvilDragon

KSP Wizard
Yeah I wouldn't necessarily make a sweeping argument that all internal variables are inherently safe in wait() scenarios...
 
Top Bottom