Thanks 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.