Focus on what matters: use managed-state-event-loop


#1

This micro-framework fell out of my Trinkey work and is super-helpful. It allows you to schedule stateless functions to run on a recurring basis while handling all the input/output without resorting to global storage. You can even share state across functions.

Check it out:


#2

I’m looking for a method of scheduling tasks that don’t require the use of a busyloop or delay. How are folks managing this in their code currently without interrupts?

For example, say you want to set a pomodoro timer, that notifies you when 45 minutes have past, and then go back to editing a function/document in the uLisp editor. How would that be accomplished as minimally as possible?

I think one could create a C function (that runs after the repl function in the main loop) which checks a global association list with the time as the key and the function as the value. The function would need to be a thunk, and have all of it’s parameters supplied by the calling function.

Any other ideas?


#3

You’re getting into OS like functionality here. There’s the two kinds of multitasking; preemptive and cooperative. uLisp doesn’t provide preemptive multitasking functionality afaik. Otoh, cooperative can be really good because you can have coroutines and closures pretty easily in uLisp. Then you’d want a framework to call them on time, which would be something like what the OP posted. I usually go with the latter style, but I haven’t done anything involving scheduling in uLisp surprisingly.


#4

Yes, and no. Yes, I would like to extend the “LispMachine” concept to be a functional lisp OS. But mostly I’m trying to work around the limitations of not having the event driven capabilities that are built into most languages. In bare metal microcontroller C, I would have interrupt handling, same with Scheme or Common Lisp.

The functionality I’m looking for is not much different than adding break to a running REPL, and evaling a thunk, and then returning to the original REPL eval. Or trampolining between two tail call optimized recursive functions, one of which is checking for a specific event, and then finally running the tail call (thunk) when it evaluates true.

Any ways, I was just wondering if anyone had a method of getting around this limitation in uLisp. We have millis, for-millis, and delay, but no way of concurrently waiting without locking up the REPL with a busy-wait.


#5

I thought a bit about this. I believe a very simple kind of cooperative multitasking could be done using a scheduler as the main infinite loop in the repl and encapsulating all other functionality within closures. This should save the context of the “tasks” and make them independent. The drawback would be that all functions of a task need to sit within that closure - but for smaller programs, why not? For larger programs this might create blocks that are hard to read, though, but the overhead would still be rather small.
Another thing I’ve been thinking of is the timer interrupt functionality of processors like the 2350. It should easily be possible to extend uLisp with small functions that set a global “time’s up” flag whenever the timer interrupt fires. This could be used by cooperative tasks as some kind of signal it’s time to give way.
All that would need to be strictly cooperative since there would be no means of protection (apart from variables within closures), and the whole idea obviously fails as soon as blocking functions like (delay) are used.
Bottom line: I think it’s possible and useful for several purposes, but on the other hand there’s not that much difference to a “main loop” that calls functions whenever a timespan measured with “millis” has passed. A scheduler as described would have very restricted means of control.

But then again I don’t think it’s a bad thing to circumvent a real OS, because IMHO this quickly leads to some kind of hard to track black box behaviour/annoying abstraction levels and the usual bureaucracy overhead I’m glad -not- to be forced to deal with using uLisp.


#6

You can see what I did when I attempted my operating system experiment uos uLisp Operating System as far as keeping programs inside closures and passing control between various applications. I don’t think it would be too hard to add a scheduler to that framework, but I could be naive. The whole difficult aspect is writing everything else in a manner that cooperates with the framework.

My ultimate goal was to write a much more full featured REPL (than the default uLisp one), but it was surprisingly hard, even harder than writing an editor. But once you have a REPL that can support getting “interrupted”, the scheduling part should be easy afaik (just call millis every time you check for a keypress and compare it to a list of tasks, or whatever scheduling algo you want to use).