This post has been republished via RSS; it originally appeared at: Microsoft Developer Blogs.
We've been working on a RunOnFiber function that creates a fiber with a large stack and runs the lambda on it. This is handy if you have a function that requires a lot of stack, and you're not sure whether your caller provided enough. The version we developed assumed that the only thing of interest that is returned from the lambda is its return value. But you might have additional context that needs to be returned, such as the Win32 last-error code or the C runtime errno. We can adapt our existing version to capture additional state about the fiber so it can be transferred to the original thread. Since this template may be used with different RetTypes, we factor out the part that is type-independent into a RunOnFiberTypeNeutral helper. There is a bit of trickiness in the helper function: We make sure to capture the error code immediately and return it explicitly. This is important because the destructors for the unique_fiber and unique_thread_as_fiber may change the value of GetLastError(), so we need to grab it while we still can. We expand the state shared with the fiber to include not only the return value of the callback, but also the GetLastError() and errno values as they were at the completion of the callback. When all is said and done, we restore the error code and errno to the current thread and then return the value that was captured from the execution of the callback. If RunOnFiberTypeNeutral fails, then we take the error code and put it into the capturedError so that the normal cleanup code will apply it to the thread before returning. I'm assuming that if anything goes wrong with CreateFiber or ConvertThreadToFiber, then the failureValue and existing Win32 error code are sufficient to explain what went wrong. I leave setting the errno as an exercise. Note that we require that the return value type be trivially copyable and trivially destructible. Trivial copyability is required so that we can return it without disturbing the Win32 last-error code or errno. Trivial destructibility is required so that we can destruct the failureValue parameter and the capturedValue without disturbing the Win32 last-error code or errno. This requirement is not a problem in practice, because a return type that requires nontrivial copy or destructor operations is going to trigger invisible code execution for temporary objects, which is likely to affect ephemeral thread-local state. In that case, you cannot rely on the Win32 last-error code or the errno value anyway, so there's no reason to try to preserve that value from the fiber back to the original thread. We do not, however, require that the return value type be trivially constructible. Instead, we accept a failureValue and use that if something goes wrong with setting up the fiber. We are careful to std::move the value around, even though that doesn't really mean anything for trivially-copyable types. But it'll come in handy later. Typically, the return type in these cases is a simple scalar, like an integer. Here's the new RunOnFiber function that accepts a failureValue: Note that the above code does not work if the lambda returns a reference. I'll leave that as an exercise. Next time, we'll look at another error-reporting mechanism: C++ exceptions.