Have you given any thought to how this would play with the Python wrappings? It sounds like I'd have to wrap every call in a PyGILState_Ensure/Release to ensure you don't re-enter the Python interpreter, but that's a blocking operation. Will this work?
I'm still unconvinced that you need fibers in the event loop for that.What if, instead, you added a new method on EventLoop (or a
not-actually-in-EventLoop fiber mechanism) like:
template<typename T, typename Func> Promise<T> runInAFiber(Func&& f)
EventLoop::evalLater should not be permitted to cause a fiber switch.I guess what I mean is that functions passed into Promise::then and
I'm fine when them scheduling a fiber, a thread, or anything else for
later execution.
On Sun, Nov 17, 2013 at 6:56 PM, Kenton Varda <temp...@gmail.com> wrote:
> On Sun, Nov 17, 2013 at 6:13 PM, Andrew Lutomirski <an...@luto.us> wrote:
>>
>> If code called from EventLoop can switch fibers, then presumably
>> fulfiller.fulfill is okay, but you'll have to look at the docs or
>> implementation of read() to see if that part is okay. IMO this
>> negates the big advantage of using Promises.
>
>
> Right, that's the tradeoff I described.
>
>>
>> If, on the other hand, it's impossible to switch fibers from inside
>> the EventLoop, then there's nothing to think about. Code written in
>> the synchronous style (using wait, etc) expects fiber switches, and
>> code that lives in Promises knows that fibers won't switch. And you
>> can call back and forth between those style with minimal fuss.
>
>
Exactly.> Yes, that's what I'm proposing.
>
> OK, I see... are you just saying that you want people to explicitly use
> thenInFiber() so that you know that if you accidentally call wait() from a
> continuation registered with then(), you'll get an exception? That makes
> sense.
--Andy
On Nov 17, 2013 8:17 PM, "Kenton Varda" <temp...@gmail.com> wrote:
>
> [re-adding capnproto cc that got lost]
>
> So I just had another thought: What if wait() requires, as a parameter, a reference to an object representing the calling fiber. This reference must be passed down the stack all the way from the callback that started the fiber. Thus, every function along the way is required to make explicit the fact that it might block, by taking a Fiber& as a parameter. Thus it's impossible to accidentally miss the fact that a particular function blocks.
>
There's a risk of cheating, but that may be minor.
What's the advantage? I think that thenInAFiber is still needed (w/o the Fiber& parameter) so that functions that don't block can nonetheless spawn fibers.
--Andy
> -Kenton
>
> On Sun, Nov 17, 2013 at 7:12 PM, Andrew Lutomirski <an...@luto.us> wrote:
>>
>> On Sun, Nov 17, 2013 at 6:56 PM, Kenton Varda <temp...@gmail.com> wrote:
>> > On Sun, Nov 17, 2013 at 6:13 PM, Andrew Lutomirski <an...@luto.us> wrote:
>> >>
>> >> If code called from EventLoop can switch fibers, then presumably
>> >> fulfiller.fulfill is okay, but you'll have to look at the docs or
>> >> implementation of read() to see if that part is okay. IMO this
>> >> negates the big advantage of using Promises.
>> >
>> >
>> > Right, that's the tradeoff I described.
>> >
>> >>
>> >> If, on the other hand, it's impossible to switch fibers from inside
>> >> the EventLoop, then there's nothing to think about. Code written in
>> >> the synchronous style (using wait, etc) expects fiber switches, and
>> >> code that lives in Promises knows that fibers won't switch. And you
>> >> can call back and forth between those style with minimal fuss.
>> >
>> >
>> > Yes, that's what I'm proposing.
>> >
>> > OK, I see... are you just saying that you want people to explicitly use
>> > thenInFiber() so that you know that if you accidentally call wait() from a
>> > continuation registered with then(), you'll get an exception? That makes
>> > sense.
>>
>> Exactly.
>>
>> --Andy
>
>
Hmm. I was imagining that you'd assume that everything could block(i.e. pretend that you're using a real thread, not a fiber), but that
could be useful too.
To me, "blocking" means "waits w/o busy-looping", which isn't reallythe same thing as "may reenter me".
It could be worth looking at how Go handles this.
On Nov 17, 2013 10:17 PM, "Kenton Varda" <temp...@gmail.com> wrote:
>
> On Sun, Nov 17, 2013 at 10:06 PM, Andrew Lutomirski <an...@luto.us> wrote:
>>
>> Hmm. I was imagining that you'd assume that everything could block
>> (i.e. pretend that you're using a real thread, not a fiber), but that
>> could be useful too.
>
>
> That doesn't really work. In a real thread, you'd use mutexes to protect your state, but you can't use mutexes with fibers because you'd deadlock. We could invent a kind of mutex explicitly for fibers which switches fibers if the lock is busy. But, arguably the whole point of using fibers instead of threads is to avoid the need for mutexes and all the errors people make with them. To avoid mutexes, people need to know exactly where reentrance can happen and handle it there, while being able to skip that extra work everywhere else.
Fair enough.
>
>>
>> To me, "blocking" means "waits w/o busy-looping", which isn't really
>> the same thing as "may reenter me".
>
>
> Fair enough. Any better ideas?
>
How about FiberSwitcher? IOW, I think the relevant part is the permission to invoke the fiber switching mechanism, rather than knowledge of what fiber you are.
It's too bad that there's no universal name for cooperatively scheduled threads. Windows has fibers, Linux has whatever the ucontext mechanism is called, Python has greenlets and some obsolete thing that I've forgotten the name of, and Javascript can't even figure out what to call a thread.
--Andy
>>
>> It could be worth looking at how Go handles this.
>
>
> I think their threads are actually preemptive, and they encourage inter-thread communication via channels. But I don't know much about Go.
>
How about FiberSwitcher? IOW, I think the relevant part is the permission to invoke the fiber switching mechanism, rather than knowledge of what fiber you are.
It's too bad that there's no universal name for cooperatively scheduled threads. Windows has fibers, Linux has whatever the ucontext mechanism is called, Python has greenlets and some obsolete thing that I've forgotten the name of, and Javascript can't even figure out what to call a thread.
Hi,
just one more data point:
We used fibers (ucontext on OS X, fibers on Win) at one point in our application, and eventually removed them. The main reason is that they don't seem to be supported very well by the OS and tools these days. Especially on OS X, we ran into hard to debug crashes where stacks where smashed. Also, there is no (easy?) way to inspect the state/stack of a fiber other than the current one in the debugger (gdb/lldb). They were obsoleted in POSIX.1-2004 and removed in POSIX.1-2008 in favor of POSIX threads.
One issue with fibers: unless you have something like SchedulerActivations (or whatever they were called), sending blocking I/O to a
separate fiber will still cause the whole thread to stall, which IMO
negates some of the advantage of using a synchronous style. You'll
still have to use a special I/O library that's fully asynchronous.
(As far as I know, everyone has given up on scheduler activations.
There might be some BSD kernels around that support them.)