@@ -6,6 +6,7 @@ import akka.pattern._
6
6
import scala .concurrent ._
7
7
import scala .concurrent .duration ._
8
8
import scala .language .postfixOps
9
+ import scala .util ._
9
10
import java .util .concurrent .atomic .AtomicBoolean
10
11
import java .util .concurrent .atomic .AtomicLong
11
12
import java .util .concurrent .ConcurrentLinkedQueue
@@ -33,9 +34,10 @@ class Selector[A](api: GopherAPI) extends PromiseFlowTermination[A]
33
34
34
35
def addTimeoutSkip (f : Skip [A ] => Option [Future [Continuated [A ]]], timeout : FiniteDuration ): Unit =
35
36
{
36
- if (timeoutRecord.isEmpty) {
37
- val locked = makeLocked(Skip (f,this ))
38
- timeoutRecord = Some (new TimeoutRecord (nOperations.get,timeout.toMillis,locked))
37
+ if (! timeoutRecord.isDefined) {
38
+ timeoutRecord.lastNOperations = nOperations.get
39
+ timeoutRecord.timeout = timeout
40
+ timeoutRecord.waiter = makeLocked(Skip (f,this ))
39
41
} else {
40
42
throw new IllegalStateException (" select must have only one timeout entry" )
41
43
}
@@ -123,6 +125,8 @@ class Selector[A](api: GopherAPI) extends PromiseFlowTermination[A]
123
125
private [this ] def unlockAfter (f: Future [Continuated [A ]], flowTermination : FlowTermination [A ], dstr : String ): Future [Continuated [A ]] =
124
126
f.transform(
125
127
next => { if (mustUnlock(dstr,flowTermination)) {
128
+ if (timeoutRecord.isDefined)
129
+ scheduleTimeout()
126
130
makeLocked(next)
127
131
} else Never
128
132
},
@@ -143,27 +147,39 @@ class Selector[A](api: GopherAPI) extends PromiseFlowTermination[A]
143
147
private [this ] def scheduleTimeout (): Unit =
144
148
{
145
149
146
- val scheduler = api.actorSystem.scheduler
147
-
148
150
def tickOperation (): Unit =
149
151
{
150
- if (! isCompleted) {
151
- for (tr <- timeoutRecord) {
152
+ if (! isCompleted && timeoutRecord.isDefined) {
152
153
val currentNOperations = nOperations.get()
153
- if (currentNOperations == tr .lastNOperations) {
154
+ if (currentNOperations == timeoutRecord .lastNOperations) {
154
155
// fire
155
- ???
156
- } else {
157
- val now = System .currentTimeMillis()
158
- val nextTime = lastOperationEnd.get() + tr.timeoutMillis
159
- tr.lastNOperations = nOperations.get()
160
- scheduler.scheduleOnce((nextTime - now).millis)(tickOperation)
156
+ timeoutRecord.waiter match {
157
+ // TODO: add timeout field to skip
158
+ case sk@ Skip (f,ft) => f(sk) foreach { futureNext =>
159
+ futureNext.onComplete {
160
+ case Success (next) => if (! isCompleted) {
161
+ next match {
162
+ case sk@ Skip (f,ft) if (ft eq this ) => timeoutRecord.waiter = sk
163
+ case other =>
164
+ timeoutRecord.waiter = Never
165
+ api.continuatedProcessorRef ! other
166
+ }
167
+ }
168
+ case Failure (ex) => if (! isCompleted) ft.doThrow(ex)
169
+ }
170
+ }
171
+ case other => api.continuatedProcessorRef ! other
172
+ }
161
173
}
162
174
}
163
- }
164
175
}
165
176
166
- tickOperation()
177
+ if (timeoutRecord.isDefined) {
178
+ // TODO: make CAS
179
+ timeoutRecord.lastNOperations = nOperations.get()
180
+ val scheduler = api.actorSystem.scheduler
181
+ scheduler.scheduleOnce(timeoutRecord.timeout)(tickOperation)
182
+ }
167
183
168
184
}
169
185
@@ -223,34 +239,33 @@ class Selector[A](api: GopherAPI) extends PromiseFlowTermination[A]
223
239
}
224
240
}
225
241
242
+ private [this ] val log = api.actorSystem.log
226
243
227
244
// false when unlocked, true otherwise.
228
245
private [this ] val lockFlag : AtomicBoolean = new AtomicBoolean (false )
229
246
230
- // when last operation was started
231
- private [this ] val lastOperationEnd : AtomicLong = new AtomicLong (0L )
232
- private [this ] var haveTimeout : Boolean = false
233
-
234
247
// number of operations, increased during each lock/unlock.
235
- // used for idle detection.
248
+ // used for idle and timeout detection
236
249
private [channels] val nOperations = new AtomicLong ();
237
250
238
251
private [this ] val waiters : ConcurrentLinkedQueue [Continuated [A ]] = new ConcurrentLinkedQueue ()
239
252
private [this ] val idleWaiters : ConcurrentLinkedQueue [Continuated [A ]] = new ConcurrentLinkedQueue ()
240
253
241
254
private [this ] class TimeoutRecord (
242
255
var lastNOperations : Long ,
243
- var timeoutMillis : Long ,
256
+ var timeout : FiniteDuration ,
244
257
var waiter : Continuated [A ]
245
- )
246
- private [this ] var timeoutRecord : Option [TimeoutRecord ] = None
258
+ ) {
259
+ def isDefined : Boolean = (waiter != Never )
260
+ }
261
+
262
+ private [this ] val timeoutRecord : TimeoutRecord = new TimeoutRecord (0L ,0 milliseconds, Never )
247
263
248
264
private [this ] val processor = api.continuatedProcessorRef
249
265
250
266
private [this ] implicit val executionContext : ExecutionContext = api.executionContext
251
267
252
268
253
-
254
269
}
255
270
256
271
0 commit comments