Remove one pair of lock() / unlock() during EventLoop run. (#235)

Motivation:

We can remove one lock() / unlock() pair by saving the next ready task during loop execution.

Modifications:

Remvoe one lock() / unlock() pair.

Result:

Less locking during EventLoop run executions.
This commit is contained in:
Norman Maurer 2018-03-26 16:42:39 +02:00 committed by GitHub
parent 6cf8ca4398
commit cce02c9e40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 17 additions and 15 deletions

View File

@ -443,19 +443,14 @@ internal final class SelectableEventLoop: EventLoop {
}
}
private func currentSelectorStrategy() -> SelectorStrategy {
// TODO: Just use an atomic
tasksLock.lock()
let scheduled = scheduledTasks.peek()
tasksLock.unlock()
guard let sched = scheduled else {
// No tasks to handle so just block
private func currentSelectorStrategy(nextReadyTask: ScheduledTask?) -> SelectorStrategy {
guard let sched = nextReadyTask else {
// No tasks to handle so just block. If any tasks were added in the meantime wakeup(...) was called and so this
// will directly unblock.
return .block
}
let nextReady = sched.readyIn(DispatchTime.now())
if nextReady <= .nanoseconds(0) {
// Something is ready to be processed just do a non-blocking select of events.
return .now
@ -482,12 +477,13 @@ internal final class SelectableEventLoop: EventLoop {
task.fail(error: EventLoopError.shutdown)
}
}
var nextReadyTask: ScheduledTask? = nil
while lifecycleState != .closed {
// Block until there are events to handle or the selector was woken up
/* for macOS: in case any calls we make to Foundation put objects into an autoreleasepool */
try withAutoReleasePool {
try selector.whenReady(strategy: currentSelectorStrategy()) { ev in
try selector.whenReady(strategy: currentSelectorStrategy(nextReadyTask: nextReadyTask)) { ev in
switch ev.registration {
case .serverSocketChannel(let chan, _):
self.handleEvent(ev.io, channel: chan)
@ -504,6 +500,8 @@ internal final class SelectableEventLoop: EventLoop {
// TODO: Better locking
tasksLock.lock()
if scheduledTasks.isEmpty {
// Reset nextReadyTask to nil which means we will do a blocking select.
nextReadyTask = nil
tasksLock.unlock()
break
}
@ -512,16 +510,20 @@ internal final class SelectableEventLoop: EventLoop {
let now = DispatchTime.now()
// Make a copy of the tasks so we can execute these while not holding the lock anymore
while tasksCopy.count < tasksCopy.capacity, let task = scheduledTasks.peek(), task.readyIn(now) <= .nanoseconds(0) {
tasksCopy.append(task.task)
_ = scheduledTasks.pop()
while tasksCopy.count < tasksCopy.capacity, let task = scheduledTasks.peek() {
if task.readyIn(now) <= .nanoseconds(0) {
_ = scheduledTasks.pop()
tasksCopy.append(task.task)
} else {
nextReadyTask = task
break
}
}
tasksLock.unlock()
// all pending tasks are set to occur in the future, so we can stop looping.
if tasksCopy.count == 0 {
if tasksCopy.isEmpty {
break
}