-
Notifications
You must be signed in to change notification settings - Fork 2
move ExecutionContext from Promise to app callbacks #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: wip-sip14-fixes
Are you sure you want to change the base?
Conversation
These tests currently check that the implicit ExecutionContext is not used by Future callback methods like onSuccess and map.
- declare the invariant that all app callbacks have an associated ExecutionContext provided at the place the callback is passed to a method on Future - always run callbacks in their associated EC - since all callbacks have their own EC, Promise does not need one - "internal" callbacks don't need to defer execution either since we know the ultimate app callback will do so, therefore we can use an immediate executor for these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how this will fly. Wasn't your point that you wanted an application executor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is that modulo bugs, this executor is only used in the lexical scope of the Future class, it's never used for the app's callbacks.
By "app" I just mean "code outside the Future implementation"
Another way to look at it is that if the app has any callbacks on the Future, there is always an app executor "after" this internal one (associated with the app callback). You can see later in the code that in methods that take an implicit executor from the app, that executor is manually used rather than this internal one.
So this internal EC should be an undetectable implementation detail. I tried a different way to do it (adding virtual methods to use rather than the existing onComplete) but I think this way is less code and functionally equivalent.
It also adds the elegance that the patch uses its own new feature (lexically-scoped custom executors) to implement itself - kind of fun!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this executor (see below).
They said the callback ran either immediately or in the provided ExecutionContext, but really the callback is guaranteed to be run in the ExecutionContext. However ExecutionContext.execute() may be run either immediately or asynchronously from some other ExecutionContext.
Attempt to add some more clarity about why it exists and why it is safe, for future readers of the code.
|
A couple related follow-on changes are in https://github.com/havocp/scala/commits/havocp-execution-context-2 but I'm not adding them to this pull request (unless you want them here) since they are separable. The follow-on changes add the withDefaultExecutionContext for Java and tweak the _taskStack batching to avoid batching across executors. I'd send a new pull with those follow-on changes if you want them after we sort out this pull. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if one of these throws an exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same thing that happened before, the dispatchFuture/notifyCompleted are still used and still catch exceptions. They are just moved below into bindDispatch(), i.e. the closure that used to be created here is created "in advance" because we need to capture the executor in onComplete rather than capturing it here.
(I reworked this a bit in the follow-on branch I mentioned in comments (not in this pull request yet), the rework may make this clearer but isn't intended to change the behavior. This patch makes the more minimal and straightforward change and avoids the potentially more controversial rework.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I was thrown off by the fuzziness of "this assumes ..."
|
Hey, thanks for merging! There is still an important bugfix in havocp@1c8fc6d Should I do a pull vs. phaller/scala or scala/scala for that? |
|
Hey Havoc, No worries- thanks for your pull request. It spawned a lot of useful discussion and we have high hopes for the changes. Concerning the bugfix: could you please submit a pull vs. scala/scala master? Also, it would be best if you would put in a "review by @axel22", just because I'm off the grid for a few days. Thanks! On Jun 1, 2012, at 12:25 AM, Havoc Pennington wrote:
|
|
Yep, I'll review this. |
In Scala 2.8.2, an optimization was added to create a static
cache for Symbol literals (ie, the results of `Symbol.apply("foo"))`.
This saves the map lookup on the second pass through code.
This actually was broken somewhere during the Scala 2.10 series,
after the addition of an overloaded `apply` method to `Symbol`.
The cache synthesis code was made aware of the overload and brought
back to working condition recently, in scala#3149.
However, this has uncovered a latent bug when the Symbol literals are
defined with traits.
One of the enclosed tests failed with:
jvm > t8933b-run.log
java.lang.IllegalAccessError: tried to access field MotherClass.symbol$1 from class MixinWithSymbol$class
at MixinWithSymbol$class.symbolFromTrait(A.scala:3)
at MotherClass.symbolFromTrait(Test.scala:1)
This commit simply disables the optimization if we are in a trait.
Alternative fixes might be: a) make the static Symbol cache field
public / b) "mixin" the static symbol cache. Neither of these
seem worth the effort and risk for an already fairly situational
optimization.
Here's how the optimization looks in a class:
% cat sandbox/test.scala; qscalac sandbox/test.scala && echo ":javap C" | qscala;
class C {
'a; 'b
}
Welcome to Scala version 2.11.5-20141106-145558-aa558dce6d (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :javap C
Size 722 bytes
MD5 checksum 6bb00189166917254e8d40499ee7c887
Compiled from "test.scala"
public class C
{
public static {};
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #16 // Field scala/Symbol$.MODULE$:Lscala/Symbol$;
3: ldc scala#18 // String a
5: invokevirtual scala#22 // Method scala/Symbol$.apply:(Ljava/lang/String;)Lscala/Symbol;
8: putstatic scala#26 // Field symbol$1:Lscala/Symbol;
11: getstatic #16 // Field scala/Symbol$.MODULE$:Lscala/Symbol$;
14: ldc scala#28 // String b
16: invokevirtual scala#22 // Method scala/Symbol$.apply:(Ljava/lang/String;)Lscala/Symbol;
19: putstatic scala#31 // Field symbol$2:Lscala/Symbol;
22: return
public C();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial scala#34 // Method java/lang/Object."<init>":()V
4: getstatic scala#26 // Field symbol$1:Lscala/Symbol;
7: pop
8: getstatic scala#31 // Field symbol$2:Lscala/Symbol;
11: pop
12: return
}
fixup
No description provided.