Simplify running code

This commit is contained in:
Cory Benfield 2018-01-08 16:39:54 +00:00
parent 34e0b1ac0a
commit 8ee527ac52
6 changed files with 144 additions and 25 deletions

View File

@ -16,11 +16,9 @@ import Dispatch
public class EmbeddedEventLoop : EventLoop {
let queue = DispatchQueue(label: "embeddedEventLoopQueue", qos: .utility)
public var inEventLoop: Bool {
return true
}
var isRunning: Bool = false
var tasks = CircularBuffer<() -> ()>(initialRingCapacity: 2)
@ -46,25 +44,15 @@ public class EmbeddedEventLoop : EventLoop {
// We're not really running a loop here. Tasks aren't run until run() is called,
// at which point we run everything that's been submitted. Anything newly submitted
// either gets on that train if it's still moving or
// either gets on that train if it's still moving or waits until the next call to run().
public func execute(task: @escaping () -> ()) {
queue.sync {
if isRunning && tasks.isEmpty {
task()
} else {
tasks.append(task)
}
}
tasks.append(task)
}
func run() throws {
queue.sync {
isRunning = true
// Execute all tasks that are currently enqueued.
while !tasks.isEmpty {
tasks.removeFirst()()
}
func run() {
// Execute all tasks that are currently enqueued.
while !tasks.isEmpty {
tasks.removeFirst()()
}
}
@ -73,10 +61,15 @@ public class EmbeddedEventLoop : EventLoop {
}
public func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) {
queue.async {
run()
queue.sync {
callback(nil)
}
}
deinit {
precondition(tasks.isEmpty, "Embedded event loop freed with unexecuted tasks!")
}
}
class EmbeddedChannelCore : ChannelCore {

View File

@ -42,6 +42,7 @@ import XCTest
testCase(ClientSNITests.allTests),
testCase(EchoServerClientTest.allTests),
testCase(EmbeddedChannelTest.allTests),
testCase(EmbeddedEventLoopTest.allTests),
testCase(EventLoopFutureTest.allTests),
testCase(EventLoopTest.allTests),
testCase(FileRegionTest.allTests),

View File

@ -281,7 +281,7 @@ class SniHandlerTest: XCTestCase {
while buffer.readableBytes > 0 {
let writeableData = buffer.readSlice(length: 1)!
try channel.writeInbound(data: writeableData)
try loop.run()
loop.run()
XCTAssertNil(channel.readInbound())
try channel.pipeline.assertContains(handler: handler)
@ -296,7 +296,7 @@ class SniHandlerTest: XCTestCase {
// Now we're going to complete the promise and run the loop. This should cause the complete
// ClientHello to be sent on, and the SniHandler to be removed from the pipeline.
continuePromise.succeed(result: ())
try loop.run()
loop.run()
let writtenBuffer: ByteBuffer = channel.readInbound()!
let writtenData = writtenBuffer.getData(at: writtenBuffer.readerIndex, length: writtenBuffer.readableBytes)
@ -325,7 +325,7 @@ class SniHandlerTest: XCTestCase {
// Ok, let's go.
try channel.writeInbound(data: buffer)
try loop.run()
loop.run()
// The callback should have fired, but the handler should not have
// sent on any data and should still be in the pipeline.
@ -336,7 +336,7 @@ class SniHandlerTest: XCTestCase {
// Now we're going to complete the promise and run the loop. This should cause the complete
// ClientHello to be sent on, and the SniHandler to be removed from the pipeline.
continuePromise.succeed(result: ())
try loop.run()
loop.run()
let writtenBuffer: ByteBuffer? = channel.readInbound()
if let writtenBuffer = writtenBuffer {

View File

@ -160,12 +160,12 @@ class ChannelPipelineTest: XCTestCase {
let channel = EmbeddedChannel()
_ = try channel.close().wait()
let loop = channel.eventLoop as! EmbeddedEventLoop
try loop.run()
loop.run()
XCTAssertTrue(loop.inEventLoop)
do {
try channel.writeOutbound(data: FileRegion(descriptor: -1, readerIndex: 0, endIndex: 0))
try loop.run()
loop.run()
XCTFail("we ran but an error should have been thrown")
} catch let err as ChannelError {
XCTAssertEqual(err, .ioOnClosedChannel)

View File

@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
///
/// EmbeddedEventLoopTest+XCTest.swift
///
import XCTest
///
/// NOTE: This file was generated by generate_linux_tests.rb
///
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
///
extension EmbeddedEventLoopTest {
static var allTests : [(String, (EmbeddedEventLoopTest) -> () throws -> Void)] {
return [
("testExecuteDoesNotImmediatelyRunTasks", testExecuteDoesNotImmediatelyRunTasks),
("testExecuteWillRunAllTasks", testExecuteWillRunAllTasks),
("testExecuteWillRunTasksAddedRecursively", testExecuteWillRunTasksAddedRecursively),
("testTasksSubmittedAfterRunDontRun", testTasksSubmittedAfterRunDontRun),
("testShutdownGracefullyRunsTasks", testShutdownGracefullyRunsTasks),
]
}
}

View File

@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import NIO
import XCTest
public class EmbeddedEventLoopTest: XCTestCase {
func testExecuteDoesNotImmediatelyRunTasks() throws {
var callbackRan = false
let loop = EmbeddedEventLoop()
loop.execute { callbackRan = true }
XCTAssertFalse(callbackRan)
loop.run()
XCTAssertTrue(callbackRan)
}
func testExecuteWillRunAllTasks() throws {
var runCount = 0
let loop = EmbeddedEventLoop()
loop.execute { runCount += 1 }
loop.execute { runCount += 1 }
loop.execute { runCount += 1 }
XCTAssertEqual(runCount, 0)
loop.run()
XCTAssertEqual(runCount, 3)
}
func testExecuteWillRunTasksAddedRecursively() throws {
var sentinel = 0
let loop = EmbeddedEventLoop()
loop.execute {
// This should execute first.
XCTAssertEqual(sentinel, 0)
sentinel = 1
loop.execute {
// This should execute third.
XCTAssertEqual(sentinel, 2)
sentinel = 3
}
}
loop.execute {
// This should execute second.
XCTAssertEqual(sentinel, 1)
sentinel = 2
}
XCTAssertEqual(sentinel, 0)
loop.run()
XCTAssertEqual(sentinel, 3)
}
func testTasksSubmittedAfterRunDontRun() throws {
var callbackRan = false
let loop = EmbeddedEventLoop()
loop.execute { callbackRan = true }
XCTAssertFalse(callbackRan)
loop.run()
loop.execute { callbackRan = false }
XCTAssertTrue(callbackRan)
loop.run()
XCTAssertFalse(callbackRan)
}
func testShutdownGracefullyRunsTasks() throws {
var callbackRan = false
let loop = EmbeddedEventLoop()
loop.execute { callbackRan = true }
XCTAssertFalse(callbackRan)
XCTAssertNoThrow(try loop.syncShutdownGracefully())
XCTAssertTrue(callbackRan)
}
}