Skip to content

Commit

Permalink
Remove one pair of lock() / unlock() during EventLoop run.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
normanmaurer committed Mar 26, 2018
1 parent 6cf8ca4 commit 4f6bf76
Showing 1 changed file with 18 additions and 15 deletions.
33 changes: 18 additions & 15 deletions Sources/NIO/EventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
}
Expand All @@ -512,16 +510,21 @@ 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
}
}

let cnt = tasksCopy.count
tasksLock.unlock()

// all pending tasks are set to occur in the future, so we can stop looping.
if tasksCopy.count == 0 {
if cnt == 0 {
break
}

Expand Down

0 comments on commit 4f6bf76

Please sign in to comment.