3 ms·
> I want to combine the benefits of kernel threads with coroutines or goroutines/green threads/lightweight threads. (If anybody knows anything specifically abo
by opportune 3y ago
> I want to combine the benefits of kernel threads with coroutines or goroutines/green threads/lightweight threads. (If anybody knows anything specifically about fibers, I'd appreciate you telling me that because I'm not familiar with them.)
Fibers mean a couple different things these days. The basics are usually things like cooperative scheduling/different context switching and pre-emption semantics (using special synchronization libraries) that make it so you can have huge numbers of active thread-like (fibrous) things with less overhead. They also usually support a tree-like structure that enables things like cancellation. Sometimes they re-use p_threads (when one fiber completes, the next fiber gets the old p_thread) between fibers under the hood to amortize thread creation and destruction, or sometimes they pack multiple fibers into a single thread for further amortization and to reduce overhead.
Your
```
while (this.parent.isScheduled(this)) {
```
in the thread function itself confuses me a bit as typically you'd expect the code to just run when scheduled, not run otherwise - are you keeping all your lightweight threads running/scheduled by the OS but bit-flipping the printing from off to on based on if your scheduler determines it to be active? If so, to get the actual performance benefits, you need to keep the lightweight threads off the kernel scheduler itself
- samsquire 3y agoThanks so much for your reply and explanation of fibers. I still don't get it yet but I'll do some more investigation and ask ChatGPT. That code shall only run when the thread is scheduled, the guard is to prevent the lightweight thread blocking the kernel worker loop over lightweight threads. The lightweight threads run in worker threads and the scheduler is a different thread to that. You would have to remove a lightweight thread from kernel_thread.lightweight_threads to deschedule it completely. Presumably you'd do this if you were waiting for an event. I assume all my lightweight threads are ready to run and all want to run. But they need to relinquish the CPU when preempted by the scheduler. In the C code of preemptible-thread, the user_function() of a lightweight thread is the body of the lightweight thread, it is called by one of the kernel threads assigned to the lightweight thread runtime. The kernel thread loops over each lightweight threads assigned to that kernel thread. The scheduler thread does NOT run lightweight threads. It's just used to generate signals on a time interval. It sets flags to interrupt loops. In other words, a lightweight thread will only run until it is cooperatively preempted by the scheduler thread. I've been criticised for describing it as preemption but it's a cooperative preemption. while (this.parent.isScheduled(this)) is used to cancel the execution of that lightweight thread's execution of user_function when the scheduler toggles its scheduled & preempted flag. It's the equivalent of while (true) but in a lightweight thread context. If I used while (true), the lightweight thread would never be descheduled for another lightweight thread to run. It's a way of stopping the current thread's while loop. There is one scheduler kernel thread that does the following on each time slice (ideally a few microseconds): for kernel_thread in kernel_threads: for lightweight_thread in kernel_thread.lightweight_threads: lightweight_thread.preempt() In as many kernel thread worker thread as possible, as many as you have hardware threads. They runs lightweight threads one by one until the scheduler preempts that lightweight thread and then it moves to the next one: Kernel thread lightweight thread runner/worker. It round robins the lightweight threads. while (running) { for lightweight_thread in lightweight threads: lightweight_thread.markScheduled(true) #### between here and while (lightweight_thread.isScheduled(this)) { // while i'm not preempted registerLoop(0, 0, 10000000); for (int loopVar = initialVar(0, 0); getValue(0) < getLimit(0); loopVar = increment(0)) { Math.sqrt(getValue(0)); } #### here is the lightweight thread's body. } } I've inlined the lightweight thread's body in this code above. The kernel thread scheduler loop over lightweight threads, preempt all currently running lightweight threads. While the kernel worker thread loops over that thread's lightweight threads and loops while they are not preempted. Cancellation is possible in this design. I haven't thought about removing lightweight threads from the runqueue, it's assumed every lightweight thread wants to run as much as possible. The scheduler can be enhanced to provide this.
- gpderetta 3y agoRunning a separate scheduling thread seems very wasteful and non scalable. What is your preemption condition? Can't you just increment a counter in isScheduled(this) and synchronously invoke the scheduler every N or so checks?
- samsquire 3y agoThe preemption condition is literally time. Just need to set a flag in threads after so much time. There is only one scheduling thread. That could work but that would slow down while loops of lightweight threads, but it shall work. The design is designed to avoid putting if statements inside loops for performance reasons. If you have 128 core machine then 1 thread for scheduling might be worthwhile rather than slow down 128 cores worth of work with an if statement. But is there a way to run something a timer on the kernel or something so I don't need to waste a thread? It would presumably be asleep most of the time anyway.