-
Notifications
You must be signed in to change notification settings - Fork 13
Fibers
A fiber is a lightweight object similar to a thread with its own evaluation stack, but multiple fibers cannot run parallel to each other in a single thread. While the execution of threads is scheduled by the system, the application must explicitly switch between fibers in order to run them.
SharpUtils provides two classes for creating these objects - Fiber and VirtualFiber. The former class is implemented via WinApi functions like CreateFiber etc., while VirtualFiber is simulated using threads.
The purpose of fibers has become narrower since multiple CPUs started being more common. Fibers can be still used for things like simulating a state machine or for concurrect but synchronised tasks. In SharpUtils the only purpose of fibers is in the IterEnumerable class in Reactive collections.
The Fiber and VirtualFiber classes differ only in their implementation, and both are derived from FiberBase, so while I will stick to Fiber in this example, the other class may be used equally.
Each thread has a default fiber object associated with it which is executing the code the thread was created with. The current fiber can be obtained via Fiber.CurrentFiber
. To create a new fiber, the delegate specifying the code that should be run in the fiber must be passed in a same way like for the Thread class, allowing the same overloads.
Once the fiber is obtained, you must switch to it to start executing its code using the Switch method. Then, the thread will start executing the code in that fiber and forget about the old one. You must be careful when working with disposable objects, because no finalization routines will be run if a fiber is disposed. After the current fiber comes to its end, the thread will end as well.
Fiber main = Fiber.CurrentFiber;
Fiber alt = new Fiber(
delegate{
Console.WriteLine("Alt1");
main.Switch();
Console.WriteLine("Alt2");
main.Switch();
Console.WriteLine("Alt3");
main.Switch();
}
);
Console.WriteLine("Main1");
alt.Switch();
Console.WriteLine("Main2");
alt.Switch();
Console.WriteLine("Main3");
alt.Switch();
Console.WriteLine("Main4");
The code will resume the method in the alt
thread three times, and exits via the main fiber.
For library code, you should always allow the consumer to specify what kind of fibers should be used. As specified earlier, the two implementations of Fiber and VirtualFiber are equivalent in terms of behaviour. Therefore, if fiber semantics is required, the code should take an IFiberFactory which represents the type of fibers that should be used, either Fiber.Factory or VirtualFiber.Factory.
An example of generic fiber usage is in IterEnumerable:
public Enumerator(Action<Func<T, bool>> enumProc, IFiberFactory fiberFactory)
{
this.fiberFactory = fiberFactory;
enumFiber = fiberFactory.CreateNew(
()=>{
enumProc(FiberNext);
state = -1;
mainFiber.Switch();
}
);
}
bool FiberNext(T obj)
{
Current = obj;
state = 1;
mainFiber.Switch();
return state != -1;
}
public bool MoveNext()
{
if(state == 0 || state == 1)
{
mainFiber = fiberFactory.CurrentFiber;
try{
enumFiber.Switch();
}finally{
mainFiber = null;
}
if(state == 1)
{
return true;
}
enumFiber.Dispose();
return false;
}
return false;
}