Sign in to follow this  
floatingwoods

How to switch fibre from a thread?

Recommended Posts

Hello,

I am wondering if this is possible, and if yes how to do it:

I have an application that can launch several fibres. Normally, I switch from one fibre to another (or back to the main fibre) "from within" the current fibre with the command: SwitchToFiber

In rare situations, I also need to be able to have the fibre switching controlled by an auxiliary thread (e.g. emergency thread), i.e. switch fibres "from outside".

Thanks for any insight!

Marc

Share this post


Link to post
Share on other sites
Quote:
Original post by floatingwoods
I am wondering if this is possible

Nope! The whole point of fibers is cooperative multithreading. You can have the auxiliary thread communicate with the fiber and ask it to switch (as long as the fiber is listening, of course), but you can't force it to switch. If you want that, it's a pretty good indication that fibers aren't appropriate for your application.

Share this post


Link to post
Share on other sites
If you're on Windows, you could try using QueueUserAPC and have it switch fibres when the thread gets to a point where it checks the APC queue. No guarantees that this will do what you want, though.

The code that you want to interrupt with an APC must call one of the "alertable wait functions" (like Sleep or WaitFor___Objects) - it checks the APC queue at that time.

So what might work is have a wrapper function to switch fibers:


void SwitchToFiberOrAPC(etc)
{
Sleep(0);
SwitchToFiber(etc);
}


You'd have to wait for the current fiber to yield before the APC executed, though.

I haven't actually tried this in practice, but it seems like it would behave more-or-less how you want.

Share this post


Link to post
Share on other sites
If you want, you could also just reverse engineer some of the fiber-related functions. They're extremely simple.

They first get the linear address of the TIB using fs:[0x18]

The TIB stores the currently active fiber pointer at TIB+0x10.

The fiber pointer points to a 756 byte memory block which is allocated on the heap when you call ConvertThreadToFiber or CreateFiber.

The fiber struct stores a bunch of stuff necessary for switching, for example:

[fiber+0x30] = saved FPU control word
[fiber+0x34] = saved FPU status word
[fiber+0xB0] = saved edi
[fiber+0xB4] = saved esi
[fiber+0xB8] = saved ebx
[fiber+0xC8] = saved ebp

etc...

SwitchToFiber does nothing but save/restore registers and other processor state via the fiber struct.


So theoretically all you'd need to do would be:

SuspendThread
GetThreadContext
Read fs:[0x18] to get that thread's TIB pointer
Read [TIB+0x10] to get that thread's current fiber struct pointer
Manually fake the thread's call-into-SwitchToFiber by pushing one DWORD of junk (for the fiber pointer) and the current EIP onto that thread's stack.
Copy the thread context (along with your adjusted ESP) into the existing fiber struct
Copy the new fiber struct into the thread context and do the reverse ESP/EIP adjustment to immediately restore the thread to its post-SwitchToFiber state.
ResumeThread


You'd have to do this separately for 32-bit and 64-bit processes, too. Thread contexts are totally different between the two, so I expect the fiber struct would be as well.


Also, it looks like volatile registers (eax, ecx, edx) aren't being saved. The way the compiler works is it assumes that these registers get destroyed by any function call, so it's OK that SwitchToFiber doesn't save/restore them. HOWEVER, if you preemptively force a fiber switch, the thread might have been executing code that was using eax, ecx, and edx. That means you'll have to save those registers (and possibly more) somewhere else. It also means that, to switch back to the fiber, you cannot use SwitchToFiber - you MUST restore it using your "hack" mechanism so that it can restore volatile registers.

Considering you would not be able to easily prevent the new fiber from SwitchToFiber'ing back to the original fiber, this could only be used as a one-way ticket, as far as the fiber you're switching to is concerned.


One workaround would be to implement a complete replacement fiber system from scratch (in assembly) that saves all registers. It seems like it would be pretty easy, actually.

[Edited by - Nypyren on December 11, 2010 4:45:23 PM]

Share this post


Link to post
Share on other sites
Thanks a lot for the lengthly explanation Nypyren!

The method you proposed is however a bit too complex for an average programmer as myself I am afraid ;)

I instead decided to program a thread scheduler that behaves in a similar way as my current fibers. And indeed, it works fine, however with a 10% speed decrease. That might not be a lot, but I am wondering if I could optimize something. What I am currently doing is:

- Create threads one at a time and let it run freely until it calls the switchToAnotherThread function. A thread who called that function will flag the next thread in a list for execution, then call SwitchToThread() and sit there until it is itself flagged for execution.

- This means that only one thread is running at a time and the other threads are simply idle (waiting until they get flagged for execution by the running thread that will itself become idle). This allows me to keep the exact same architecture as with fibers without complicated resource locking

While a thread is idle, it performs following code:


while (!executionFlagIsSet)
{
if (SwitchToThread()==0)
Sleep(1);
}



Above piece of code doesn't seem optimized to me for following reason:

If SwitchToThread() returns 0, this means that the current processor core cannot handle another thread. However there is another thread running on another processor core that maybe could. So, since in my case there won't be more than one thread active at the same time anyway, is there a way to tell the OS to have all my threads handled by the same processor core?

But as mentionned above, the threaded version of my application runs with just a 10% speed decrease (well, I have just tested it with 5 threads or so, maybe that decrease might get more important if I have more threads). But I feel annoyed by the Sleep(1) above.

Thanks for any insight!

Share this post


Link to post
Share on other sites
I've messed around with using threads like fibers before. Here's what I did:

- I gave each thread a mutex which is also accessible by other threads.

- When I wanted to switch to a different thread, I signaled the mutex of the other thread, and then immediately called WaitForSingleObject on the current thread's mutex, which put itself to sleep. I do this in a function that is called in the same way as SwitchToFiber - I pass it the thread to switch to, and it figures out which mutex to signal.


This behaves nearly the same as fibers - I have all of the threads waiting on their mutex except for one. The threads can each be scheduled on any processor, so it works around the issue you mentioned where SwitchToThread might not yield.


You're free to control the threads however you want in this way. For example, you could keep some kind of central queue of threads, and have your switching function wake up the first one in the queue (instead of explicitly letting thread A wake up thread B when it yields).


You won't need to, but the function to force a thread to be scheduled on a specific processor is "SetThreadAffinityMask". I've never been forced to use this function yet, though.

[Edited by - Nypyren on December 12, 2010 3:46:55 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this