Create a StackSaver class to facilitate State Management
Today the C# compiler is capable of creating a State Machine for us when a method returns IEnumerator or IEnumerable.
But, the state machine itself can be useful for void methods, but that's not all. Methods written that way must have a specific return type. I think it can be better if a class is created that "saves the stack values" and returns to the caller when the "YieldReturn" method is called (it can have a void version and a generic version for the return type).
Its usage could be like:
var stackSaver = new StackSaver(someDelegate);
At any point I can call:
It will run the method pointed by someDelegate normally, but when that method calls:
StackSaver.StatcYieldReturn or stackSaver.YieldReturn(void or with some value) it saves the stack values (as they are already managed) and returns to the stackSaver.MoveNext next line.
When calling it again, it restores the stack and continues. This will allow for better state management, as the YieldReturn itself could be passed as a delegate, and so the same code may be "yield returning" if the delegate is the YieldReturn one or may be simple waiting some time if it is pointing to a Thread.Sleep.
Surely this will help games (Update method in special) a lot, but will be useful for enumerators and will be more versatile than the actual yield return, and will be available to any .Net compiler without compiler modification.
Yorye N commented
The easy way to get that behavior now is making a function like so:
public IEnumerable<bool> ReadData(Stream dataStream, ref bool stop)
yield return true;
and usage would be:
var myStream = // some stream
var stop = false;
while (ReadData(myStream, ref stop))
stop = CheckIsEnd(myStream);
And you can even use it as as an Enumerator:
var readDataEnumerator = ReadData(myStream, ref stop).GetEnumerator();
this.DataRecieved += (sender, args) => readDataEnumerator.MoveNext();
But it would be very nice if we could do something like this:
public ICodeRunner ReadData(Stream dataStream)
and use it like:
var readDataRunner = ReadData(myStream); // maybe we want .AsCodeRunner() here?
this.DataRecieved += (sender, args) => readDataRunner.RunNext();
// if the underlying function has finished executing, RunNext() will either throw a function or do nothing
and maybe even `readDataRunner.RunTimes(10);` or `readDataRunner.RunUntil(() => SomePredicate());` and maybe even Aync versions! :D
Only adding a new link to the compiler/interpreter that uses that concept:
I also published an article on how the same resource (the StackSaver, Coroutine or ManagedFibers) could be used to do the same job of the async/await pair, but without requiring specific return types (which also makes general-purpose interfaces more prepared for future changes, as you don't risk breaking your code if some implementation needs to be "awaitable").
The article is at: http://www.codeproject.com/Articles/357724/Async-Await-Could-Be-Better
First triple vote I used. Adding this into .NET5 or .NET6 would make Node.js a TOTAL JOKE compared to .NET
After many research, I finally understood the right names.
So, the StackSaver class that I am commenting may very well be named Coroutine. I already implemented a StackSaver class using full-threads, but I am sure it will be better avoiding new real-threads. Also, I did find that Fibers already allow such "stack-saving" and the creation of coroutines in the normal windows API, unfortunatelly it does not work in .Net even with p/invokes (ok, it works for some time... but then it crashes... I am sure it is related to garbage collection as the .Net does not see the alternative and unmanaged callstacks).
So, I should put it here: Add managed Fibers to .Net.
In fact, my implementation on the matter (my POLAR compiler/interpreter) only updates a reference (a pointer). So, you are right, it will not copy the stack, it can only update a single pointer and make everything work.
I am voting for this with the assumption that it can be done with high performance, e.g. by not actually copying the stack but by changing the stack pointer.
I suppose the stack size may have to be a multiple of the processor page size in order to work this way; so perhaps the user could request that when the stack is small (for a user-defined threshold, such as under 1KB) the saved stack should be copied to the main stack for execution, so that a full page of storage is not required. That would be most useful when stacks are long-lived and numerous, such as those that represent actors in a game. For temporary stacks such as one that represents an enumerator, it makes more sense to allocate a full 4K page (with an uncommitted 4K guard page afterward) even if the stack only uses 100 bytes, since only a few enumerators will be allocated at any given time.
I am actually creating a new compiler/interpreter and I am using it to prove the concept. You can see it working here: http://www.paulozemek.com/Polarlight.TestPage.aspx
It uses the "stacksaver" for a method, and than yield returns at any moment, be it under finally blocks, be it from inner methods. In either case, it does not have any special return type.
DeonHe - MSFT commented
Thank you all for voting on this idea. I have moved it to the team that can better address this issue.