Well, the plan for me wasn't quite continuations because the idea isn't to throw them away each time though I suppose you could since they're rather cheap. My plan was to keep them by default so that you could pre-empt them and use them for multiplexing as well.
The idea was to then furnish a separate ring 0 and ring 3 change_thread(), and perhaps put the change_thread() code in execute-only pages for each agent.
The change_thread() would pre-emptively iterate through their continuation stack in a round-robin fashion, which is okay because we move up and down using interrupt prioritization via the LAPIC.
The continuations are being stored by pushing everything onto the continuation's (users) stack and storing the RSP on the continuation stack (belonging to us). I then put a separate push on there for status information.
So anyways, now it looks like this:
Code: Select all
struct Continuation {
ulong flags =
[63..3 = signal I'm waiting for]
[2 = is waiting for a signal]
[1 = is awake]
[0 = is alive];
ulong RSP;
}
So yes, my plan then uses pervasive continuations, as event handlers at entry points for IPC and RPC, and for doing various things inside a "process/agent". In fact, the plan was to do this exclusively.
Things I encountered so far:
1)
I realized you can't push status data on active threads or mark continuations dead very effectively if you try to do it by storing it on the user thread's stack. The initial plan was to push it just before it went to sleep, and pop it every time just after it woke up and before it resumed.
2)
Killing threads can be done simply by setting the bit, which is checked during change_thread. However, that doesn't clear it off the stack. An innovation I came up with to remedy the dirtiness was to, when executing change_thread(), store the old stack back if that one was dead. It causes the algorithm to slowly shift all dead stacks to the bottom, where the RSP simply adjusts.
3)
I think the signal thing is kind of cool. It evolved from an explanation I made on the idea of one thread ceding to another thread. It's asynchronous if you keep running, or synchronous if you then cooperatively give up this CPU session.
4)
State changes on threads that may be continuated or awake are asynchronous atm. I'm thinking you'd have to pre-empt it, since it couldn't possibly be aware of the state change synchronously while running.
Definitely would like some more exploration into this subject.
For example, perhaps more on why you feel that discarding continuations after one use is better than keeping them?