Using fibers to expand a thread’s stack at runtime, part 3

This post has been republished via RSS; it originally appeared at: Microsoft Developer Blogs.

We've been working on a Run­On­Fiber 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 Run­On­Fiber­Type­Neutral 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 Get­Last­Error(), 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 Get­Last­Error() 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 Run­On­Fiber­Type­Neutral 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 Create­Fiber or Convert­Thread­To­Fiber, 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 Run­On­Fiber function that accepts a failure­Value: 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.