@@ -32,8 +32,8 @@ import scala.annotation.implicitNotFound
32
32
* While it is possible to simply import
33
33
* `scala.concurrent.ExecutionContext.Implicits.global` to obtain an
34
34
* implicit `ExecutionContext`, application developers should carefully
35
- * consider where they want to set execution policy;
36
- * ideally, one place per application— or per logically related section of code—
35
+ * consider where they want to define the execution policy;
36
+ * ideally, one place per application — or per logically related section of code —
37
37
* will make a decision about which `ExecutionContext` to use.
38
38
* That is, you will mostly want to avoid hardcoding, especially via an import,
39
39
* `scala.concurrent.ExecutionContext.Implicits.global`.
@@ -57,12 +57,12 @@ import scala.annotation.implicitNotFound
57
57
* knowing that only that library's network operations will be affected.
58
58
* Application callback execution can be configured separately.
59
59
*/
60
- @ implicitNotFound(""" Cannot find an implicit ExecutionContext. You might pass
60
+ @ implicitNotFound(""" Cannot find an implicit ExecutionContext. You might add
61
61
an (implicit ec: ExecutionContext) parameter to your method.
62
62
63
63
The ExecutionContext is used to configure how and on which
64
- thread pools Futures will run, so the specific ExecutionContext
65
- that is selected is important.
64
+ thread pools asynchronous tasks (such as Futures) will run,
65
+ so the specific ExecutionContext that is selected is important.
66
66
67
67
If your application does not define an ExecutionContext elsewhere,
68
68
consider using Scala's global ExecutionContext by defining
@@ -121,23 +121,81 @@ trait ExecutionContextExecutorService extends ExecutionContextExecutor with Exec
121
121
*/
122
122
object ExecutionContext {
123
123
/**
124
- * The explicit global ` ExecutionContext`. Invoke `global` when you want to provide the global
125
- * `ExecutionContext` explicitly.
124
+ * The global [[ ExecutionContext ]]. This default `ExecutionContext` implementation is backed by a work-stealing thread
125
+ * pool. It can be configured via the following system properties:
126
126
*
127
- * The default `ExecutionContext` implementation is backed by a work-stealing thread pool.
128
- * It can be configured via the following [[scala.sys.SystemProperties ]]:
129
- *
130
- * `scala.concurrent.context.minThreads` = defaults to "1"
131
- * `scala.concurrent.context.numThreads` = defaults to "x1" (i.e. the current number of available processors * 1)
132
- * `scala.concurrent.context.maxThreads` = defaults to "x1" (i.e. the current number of available processors * 1)
133
- * `scala.concurrent.context.maxExtraThreads` = defaults to "256"
127
+ * - `scala.concurrent.context.minThreads` = defaults to "1"
128
+ * - `scala.concurrent.context.numThreads` = defaults to "x1" (i.e. the current number of available processors * 1)
129
+ * - `scala.concurrent.context.maxThreads` = defaults to "x1" (i.e. the current number of available processors * 1)
130
+ * - `scala.concurrent.context.maxExtraThreads` = defaults to "256"
134
131
*
135
132
* The pool size of threads is then `numThreads` bounded by `minThreads` on the lower end and `maxThreads` on the high end.
136
133
*
137
134
* The `maxExtraThreads` is the maximum number of extra threads to have at any given time to evade deadlock,
138
- * see [[scala.concurrent.BlockContext ]].
135
+ * see [[scala.concurrent.blocking ]].
136
+ *
137
+ * The `global` execution context can be used explicitly, by defining an
138
+ * `implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global`, or by importing
139
+ * [[ExecutionContext.Implicits.global ]].
140
+ *
141
+ * == Batching short-lived nested tasks ==
142
+ *
143
+ * Asynchronous code with short-lived nested tasks is executed more efficiently when using
144
+ * `ExecutionContext.opportunistic` (continue reading to learn why it is `private[scala]` and how to access it).
145
+ *
146
+ * `ExecutionContext.opportunistic` uses the same thread pool as `ExecutionContext.global`. It attempts to batch
147
+ * nested task and execute them on the same thread as the enclosing task. This is ideally suited to execute
148
+ * short-lived tasks as it reduces the overhead of context switching.
149
+ *
150
+ * WARNING: long-running and/or blocking tasks should be demarcated within [[scala.concurrent.blocking ]]-blocks
151
+ * to ensure that any pending tasks in the current batch can be executed by another thread on `global`.
152
+ *
153
+ * === How to use ===
154
+ *
155
+ * This field is `private[scala]` to maintain binary compatibility. It was added in 2.13.4, code that references it
156
+ * directly fails to run with a 2.13.0-3 Scala library.
157
+ *
158
+ * Libraries should not reference this field directly because users of the library might be using an earlier Scala
159
+ * version. In order to use the batching `ExecutionContext` in a library, the code needs to fall back to `global`
160
+ * in case the `opportunistic` field is missing (example below). The resulting `ExecutionContext` has batching
161
+ * behavior in all Scala 2.13 versions (`global` is batching in 2.13.0-3).
162
+ *
163
+ * {{{
164
+ * implicit val ec: scala.concurrent.ExecutionContext = try {
165
+ * scala.concurrent.ExecutionContext.getClass
166
+ * .getDeclaredMethod("opportunistic")
167
+ * .invoke(scala.concurrent.ExecutionContext)
168
+ * .asInstanceOf[scala.concurrent.ExecutionContext]
169
+ * } catch {
170
+ * case _: NoSuchMethodException =>
171
+ * scala.concurrent.ExecutionContext.global
172
+ * }
173
+ * }}}
174
+ *
175
+ * Application authors can safely use the field because the Scala version at run time is the same as at compile time.
176
+ * Options to bypass the access restriction include:
139
177
*
140
- * @return the global `ExecutionContext`
178
+ * 1. Using a structural type (example below). This uses reflection at run time.
179
+ * 1. Writing a Scala `object` in the `scala` package (example below).
180
+ * 1. Writing a Java source file. This works because `private[scala]` is emitted as `public` in Java bytecode.
181
+ *
182
+ * {{{
183
+ * // Option 1
184
+ * implicit val ec: scala.concurrent.ExecutionContext =
185
+ * (scala.concurrent.ExecutionContext:
186
+ * {def opportunistic: scala.concurrent.ExecutionContextExecutor}
187
+ * ).opportunistic
188
+ *
189
+ * // Option 2
190
+ * package scala {
191
+ * object OpportunisticEC {
192
+ * implicit val ec: scala.concurrent.ExecutionContext =
193
+ * scala.concurrent.ExecutionContext.opportunistic
194
+ * }
195
+ * }
196
+ * }}}
197
+ *
198
+ * @return the global [[ExecutionContext ]]
141
199
*/
142
200
final lazy val global : ExecutionContextExecutor = impl.ExecutionContextImpl .fromExecutor(null : Executor )
143
201
@@ -166,14 +224,25 @@ object ExecutionContext {
166
224
override final def reportFailure (t : Throwable ): Unit = defaultReporter(t)
167
225
}
168
226
227
+ /**
228
+ * See [[ExecutionContext.global ]].
229
+ */
230
+ private [scala] lazy val opportunistic : ExecutionContextExecutor = new ExecutionContextExecutor with BatchingExecutor {
231
+ final override def submitForExecution (runnable : Runnable ): Unit = global.execute(runnable)
232
+
233
+ final override def execute (runnable : Runnable ): Unit =
234
+ if ((! runnable.isInstanceOf [impl.Promise .Transformation [_,_]] || runnable.asInstanceOf [impl.Promise .Transformation [_,_]].benefitsFromBatching) && runnable.isInstanceOf [Batchable ])
235
+ submitAsyncBatched(runnable)
236
+ else
237
+ submitForExecution(runnable)
238
+
239
+ override final def reportFailure (t : Throwable ): Unit = global.reportFailure(t)
240
+ }
241
+
169
242
object Implicits {
170
243
/**
171
- * The implicit global `ExecutionContext`. Import `global` when you want to provide the global
172
- * `ExecutionContext` implicitly.
173
- *
174
- * The default `ExecutionContext` implementation is backed by a work-stealing thread pool. By default,
175
- * the thread pool uses a target number of worker threads equal to the number of
176
- * [[https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors-- available processors ]].
244
+ * An accessor that can be used to import the global `ExecutionContext` into the implicit scope,
245
+ * see [[ExecutionContext.global ]].
177
246
*/
178
247
implicit final def global : ExecutionContext = ExecutionContext .global
179
248
}
0 commit comments