-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
BlockContext.scala
110 lines (100 loc) · 3.84 KB
/
BlockContext.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.concurrent
/**
* A context to be notified by [[scala.concurrent.blocking]] when
* a thread is about to block. In effect this trait provides
* the implementation for [[scala.concurrent.Await]].
* [[scala.concurrent.Await.result]] and [[scala.concurrent.Await.ready]]
* locates an instance of `BlockContext` by first looking for one
* provided through [[BlockContext.withBlockContext]] and failing that,
* checking whether `Thread.currentThread` is an instance of `BlockContext`.
* So a thread pool can have its `java.lang.Thread` instances implement
* `BlockContext`. There's a default `BlockContext` used if the thread
* doesn't implement `BlockContext`.
*
* Typically, you'll want to chain to the previous `BlockContext`,
* like this:
* {{{
* val oldContext = BlockContext.current
* val myContext = new BlockContext {
* override def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = {
* // you'd have code here doing whatever you need to do
* // when the thread is about to block.
* // Then you'd chain to the previous context:
* oldContext.blockOn(thunk)
* }
* }
* BlockContext.withBlockContext(myContext) {
* // then this block runs with myContext as the handler
* // for scala.concurrent.blocking
* }
* }}}
*/
trait BlockContext {
/** Used internally by the framework;
* Designates (and eventually executes) a thunk which potentially blocks the calling `java.lang.Thread`.
*
* Clients must use `scala.concurrent.blocking` or `scala.concurrent.Await` instead.
*
* In implementations of this method it is RECOMMENDED to first check if `permission` is `null` and
* if it is, throw an `IllegalArgumentException`.
*
* @throws IllegalArgumentException if the `permission` is `null`
*/
def blockOn[T](thunk: => T)(implicit permission: CanAwait): T
}
object BlockContext {
private[this] object DefaultBlockContext extends BlockContext {
override final def blockOn[T](thunk: => T)(implicit permission: CanAwait): T = thunk
}
/**
* The default block context will execute the supplied thunk immediately.
* @return the `BlockContext` that will be used if no other is found.
**/
final def defaultBlockContext: BlockContext = DefaultBlockContext
private[this] final val contextLocal = new ThreadLocal[BlockContext]()
private[this] final def prefer(candidate: BlockContext): BlockContext =
if (candidate ne null) candidate
else {
val t = Thread.currentThread
if (t.isInstanceOf[BlockContext]) t.asInstanceOf[BlockContext]
else DefaultBlockContext
}
/**
* @return the `BlockContext` that would be used for the current `java.lang.Thread` at this point
**/
final def current: BlockContext = prefer(contextLocal.get)
/**
* Installs a current `BlockContext` around executing `body`.
**/
final def withBlockContext[T](blockContext: BlockContext)(body: => T): T = {
val old = contextLocal.get // can be null
if (old eq blockContext) body
else {
contextLocal.set(blockContext)
try body finally contextLocal.set(old)
}
}
/**
* Installs the BlockContext `blockContext` around the invocation to `f` and passes in the previously installed BlockContext to `f`.
* @return the value produced by applying `f`
**/
final def usingBlockContext[I, T](blockContext: BlockContext)(f: BlockContext => T): T = {
val old = contextLocal.get // can be null
if (old eq blockContext) f(prefer(old))
else {
contextLocal.set(blockContext)
try f(prefer(old)) finally contextLocal.set(old)
}
}
}