HiTL;DR: I'm considering a way to deprecate v8::MaybeLocal handles from the binding layer.If a worker thread is forcibly killed by the main thread while running some V8 APIs, the V8 APIs may crash. To address the problem, we introduced v8::MaybeLocal handles 1.5 years ago and tried to rewrite all call sites to handle a case where v8::MaybeLocal handles return empty handles:v8::Local<v8::Value> result;if (!v8Call(object->Get(context, result))return nullptr; // Handle a case where the V8 API returns an empty handle.
...However, the programming pattern has introduced a lot of complexity to the code base as follows:- Most call sites don't know what to do when the V8 API fails. For example, most call sites assume that object->Get(), ToBoolean(), toV8() etc always succeeds.
- Blink is huge, and it's not realistic to propagate the failure up to all the call sites.
- It's not clear what context Blink should pass in the V8 API.
- It's nasty to write multiple lines just to call one V8 API.As a result, developers (e.g., DevTools) started violating the programming pattern or using v8CallOrCrash (which just crashes when the V8 API returns an empty v8::MaybeLocal handle). It's hard to say that v8::MaybeLocal handles are working.
So, I'm thinking about a way to deprecate v8::MaybeLocal handles. The main reason we needed the v8::MaybeLocal handles is that workers can be forcibly killed by the main thread. However, nhiroki@ recently removed the force-kill code from common paths of worker shutdown. This dramatically decreased the probability of the worker force-kill. It looks like that we no longer need the v8::MaybeLocal handles (I'm collecting UMAs to measure how rare it happens).The next step is to remove the v8::MaybeLocal handles from the code base. I think there are two options here:a) Just revert the changes we did 1.5 years ago. In other words, replace the current v8::MaybeLocal-version APIs with not-v8::MaybeLocal-version APIs.
b) Keep using the v8::MaybeLocal-version APIs, but rewrite all call sites so that Blink immediately crashes when V8 APIs return empty v8::MaybeLocal handles (we can do this using some macro).I'd prefer a), but if the V8 team wants to keep the v8::MaybeLocal APIs for some reason, I'm okay with b). However, in that case, I'd like to remove the context parameter from the v8::MaybeLocal-version APIs because it's not clear in many cases what context we should use and also the context is not used in most of the V8 APIs (e.g., ToNumber, ToString, Get, Set etc don't need the context).
On Mon, Jul 4, 2016 at 7:21 AM Kentaro Hara <har...@chromium.org> wrote:HiTL;DR: I'm considering a way to deprecate v8::MaybeLocal handles from the binding layer.If a worker thread is forcibly killed by the main thread while running some V8 APIs, the V8 APIs may crash. To address the problem, we introduced v8::MaybeLocal handles 1.5 years ago and tried to rewrite all call sites to handle a case where v8::MaybeLocal handles return empty handles:v8::Local<v8::Value> result;if (!v8Call(object->Get(context, result))return nullptr; // Handle a case where the V8 API returns an empty handle.note that this is the wrong pattern. The place should either have a TryCatch and deal with exceptions, or return an empty MaybeHandle.
...However, the programming pattern has introduced a lot of complexity to the code base as follows:- Most call sites don't know what to do when the V8 API fails. For example, most call sites assume that object->Get(), ToBoolean(), toV8() etc always succeeds.That's generally not true, however.- Blink is huge, and it's not realistic to propagate the failure up to all the call sites.Although that'd be the right thing to do :-/
- It's not clear what context Blink should pass in the V8 API.That'd be another bug in Blink then. The context is the context you want the exception to be created in.
- It's nasty to write multiple lines just to call one V8 API.As a result, developers (e.g., DevTools) started violating the programming pattern or using v8CallOrCrash (which just crashes when the V8 API returns an empty v8::MaybeLocal handle). It's hard to say that v8::MaybeLocal handles are working.Devtools should only do that for invoking methods in the debug context, where they know that if the script throws an exception, it's because of a fatal error they can't recover from.That's not true in the case of blink, where we don't have much control over the script being executed.So, I'm thinking about a way to deprecate v8::MaybeLocal handles. The main reason we needed the v8::MaybeLocal handles is that workers can be forcibly killed by the main thread. However, nhiroki@ recently removed the force-kill code from common paths of worker shutdown. This dramatically decreased the probability of the worker force-kill. It looks like that we no longer need the v8::MaybeLocal handles (I'm collecting UMAs to measure how rare it happens).The next step is to remove the v8::MaybeLocal handles from the code base. I think there are two options here:a) Just revert the changes we did 1.5 years ago. In other words, replace the current v8::MaybeLocal-version APIs with not-v8::MaybeLocal-version APIs.b) Keep using the v8::MaybeLocal-version APIs, but rewrite all call sites so that Blink immediately crashes when V8 APIs return empty v8::MaybeLocal handles (we can do this using some macro).I'd prefer a), but if the V8 team wants to keep the v8::MaybeLocal APIs for some reason, I'm okay with b). However, in that case, I'd like to remove the context parameter from the v8::MaybeLocal-version APIs because it's not clear in many cases what context we should use and also the context is not used in most of the V8 APIs (e.g., ToNumber, ToString, Get, Set etc don't need the context).Those methods need to know in which context to throw an exception in case of error.a) is not really an option - other embedders would tar & feather us.
b) is plainly wrong. Blink has to deal with exceptions, and while the terminate execution exception might be gone now, there are still several callsites that don't handle exceptions, leading to random crashes in blink.
What about option c) to propagate MaybeLocal<> throughout blink, so we finally correctly deal with exceptions?
Any thoughts?--Kentaro Hara, Tokyo, Japan
On Mon, Jul 4, 2016 at 3:01 PM, Jochen Eisinger <joc...@chromium.org> wrote:On Mon, Jul 4, 2016 at 7:21 AM Kentaro Hara <har...@chromium.org> wrote:HiTL;DR: I'm considering a way to deprecate v8::MaybeLocal handles from the binding layer.If a worker thread is forcibly killed by the main thread while running some V8 APIs, the V8 APIs may crash. To address the problem, we introduced v8::MaybeLocal handles 1.5 years ago and tried to rewrite all call sites to handle a case where v8::MaybeLocal handles return empty handles:v8::Local<v8::Value> result;if (!v8Call(object->Get(context, result))return nullptr; // Handle a case where the V8 API returns an empty handle.note that this is the wrong pattern. The place should either have a TryCatch and deal with exceptions, or return an empty MaybeHandle.That's theoretically correct, but the tricky part is 1) we don't want to minimize # of TryCatches because it's heavy but 2) we don't want to propagate the MaybeHandle to core/ and modules/ because it messes up the code base :/ I'm not sure if we want to take that risk for the theoretical benefit.
...However, the programming pattern has introduced a lot of complexity to the code base as follows:- Most call sites don't know what to do when the V8 API fails. For example, most call sites assume that object->Get(), ToBoolean(), toV8() etc always succeeds.That's generally not true, however.- Blink is huge, and it's not realistic to propagate the failure up to all the call sites.Although that'd be the right thing to do :-/I don't think it's the right thing to do. The 1.5 years of work proved that the programming pattern is not maintainable.
- It's not clear what context Blink should pass in the V8 API.That'd be another bug in Blink then. The context is the context you want the exception to be created in.Yes, and yukishiino@ is now writing a document to use correct contexts everywhere in Blink. To that goal, we want to decrease the number of places we have to pass in contexts.I don't think ToNumber, ToString, Get etc need to take contexts because what they need is the current context, not the relevant context. V8 can get the current context by isolate->current_context(), so the embedder doesn't need to pass in the context, right?As far as I look at the implementation of ToNumber, it doesn't look like it's using the passed-in context (except that CallDepthScope *re*enters the current context).- It's nasty to write multiple lines just to call one V8 API.As a result, developers (e.g., DevTools) started violating the programming pattern or using v8CallOrCrash (which just crashes when the V8 API returns an empty v8::MaybeLocal handle). It's hard to say that v8::MaybeLocal handles are working.Devtools should only do that for invoking methods in the debug context, where they know that if the script throws an exception, it's because of a fatal error they can't recover from.That's not true in the case of blink, where we don't have much control over the script being executed.So, I'm thinking about a way to deprecate v8::MaybeLocal handles. The main reason we needed the v8::MaybeLocal handles is that workers can be forcibly killed by the main thread. However, nhiroki@ recently removed the force-kill code from common paths of worker shutdown. This dramatically decreased the probability of the worker force-kill. It looks like that we no longer need the v8::MaybeLocal handles (I'm collecting UMAs to measure how rare it happens).The next step is to remove the v8::MaybeLocal handles from the code base. I think there are two options here:a) Just revert the changes we did 1.5 years ago. In other words, replace the current v8::MaybeLocal-version APIs with not-v8::MaybeLocal-version APIs.b) Keep using the v8::MaybeLocal-version APIs, but rewrite all call sites so that Blink immediately crashes when V8 APIs return empty v8::MaybeLocal handles (we can do this using some macro).I'd prefer a), but if the V8 team wants to keep the v8::MaybeLocal APIs for some reason, I'm okay with b). However, in that case, I'd like to remove the context parameter from the v8::MaybeLocal-version APIs because it's not clear in many cases what context we should use and also the context is not used in most of the V8 APIs (e.g., ToNumber, ToString, Get, Set etc don't need the context).Those methods need to know in which context to throw an exception in case of error.a) is not really an option - other embedders would tar & feather us.Would it be an option to keep both versions of APIs?
b) is plainly wrong. Blink has to deal with exceptions, and while the terminate execution exception might be gone now, there are still several callsites that don't handle exceptions, leading to random crashes in blink.I mean by b) that Blink handles exceptions only when Blink wants to handle it using TryCatch. Otherwise let the V8 API crash.
Remember that (unless we perfectly add TryCatch or propagate the v8::MaybeLocal handles to all entry points to bindings,) we're anyway crashing at some places or causing undefined behavior. I don't think that's better than crashing the V8 APIs immediately.
Remember that (unless we perfectly add TryCatch or propagate the v8::MaybeLocal handles to all entry points to bindings,) we're anyway crashing at some places or causing undefined behavior. I don't think that's better than crashing the V8 APIs immediately.Yes, but why not go for perfect code? Again, we never even tried that
What about option c) to propagate MaybeLocal<> throughout blink, so we finally correctly deal with exceptions?I don't think this is an option. The 1.5 years of work proved that it's not a good direction to go. After fixing the worker termination issue, I don't think that the theoretical benefit of perfectly propagating the exceptions will outweigh the cost.Any thoughts?--Kentaro Hara, Tokyo, Japan--Kentaro Hara, Tokyo, Japan
--
You received this message because you are subscribed to the Google Groups "platform-architecture-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to platform-architect...@chromium.org.
To post to this group, send email to platform-arc...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/platform-architecture-dev/CALjhuif%3Dr0R_TwXZSFBUXT3ETnGn2DO3ttHo%3DPrmUNjdOzVmxA%40mail.gmail.com.
Jochen, can you tell me what errors we may have if we are sure that the script itself cannot throw an exception?
Jochen, can you tell me what errors we may have if we are sure that the script itself cannot throw an exception?I guess it would be only OOM (which we don't really care; we can just crash under OOM).I guess Jochen's point is that in common cases you can't be sure that a given script never throws. For example, event object->Get can throw if the getter is overridden. ToString, ToNumber etc can potentially throw as well.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/platform-architecture-dev/CALjhuifNr%2BAd3tno65ea%3DxNwyjuazpky0%2By-FdbDmEwXkoRw%3Dg%40mail.gmail.com.
This is off-topic, but v8Call* sound a bit strange to me, as they don't call anything. I was confused when I saw v8Call first time.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/platform-architecture-dev/CABg10jwjxZF_759QbQY1ALcyXoKK%3DawsKdiCk33jEX%3D59cemOw%40mail.gmail.com.
I'm on the fence for renaming ToLocalChecked() to ToLocal(). ToLocalChecked (or ToLocalOrCrash) is easier for me to understand what can happen.
why not use the ToLocal() method instead of calling IsEmpty()?Local<Value> result;if (!SomeApi().ToLocal(&result)) return MaybeLocal<Value>(); // caller has to deal with exception// Use result
How would we avoid having a mix of the two in the codebase?