diff --git a/.gitignore b/.gitignore index 3aa5254..e25dbca 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /.build /Packages /*.xcodeproj +Package.pins diff --git a/Package.pins b/Package.pins deleted file mode 100644 index e0570d6..0000000 --- a/Package.pins +++ /dev/null @@ -1,5 +0,0 @@ -{ - "autoPin": false, - "pins": [], - "version": 1 -} diff --git a/Sources/Venice/Channel.swift b/Sources/Venice/Channel.swift index 3609315..f2f931c 100644 --- a/Sources/Venice/Channel.swift +++ b/Sources/Venice/Channel.swift @@ -21,7 +21,7 @@ import CLibdill /// ``` public final class Channel { private typealias Handle = Int32 - + private enum ChannelResult { case value(Type) case error(Error) @@ -35,7 +35,7 @@ public final class Channel { } } } - + private let handle: Handle private var buffer = List>() @@ -66,14 +66,14 @@ public final class Channel { handle = result } - + deinit { hclose(handle) } - + /// Reference to the channel which can only send. public lazy var sending: Sending = Sending(self) - + /// Reference to the channel which can only receive. public lazy var receiving: Receiving = Receiving(self) @@ -125,7 +125,7 @@ public final class Channel { return try buffer.removeFirst().getValue() } - + /// This function is used to inform the channel that no more `send` or `receive` should be /// performed on the channel. /// @@ -135,7 +135,7 @@ public final class Channel { public func done() { hdone(handle, 0) } - + /// Send-only reference to an existing channel. /// /// ## Example: @@ -151,27 +151,27 @@ public final class Channel { /// ``` public final class Sending { private let channel: Channel - + fileprivate init(_ channel: Channel) { self.channel = channel } - + /// :nodoc: public func send(_ value: Type, deadline: Deadline) throws { try channel.send(value, deadline: deadline) } - + /// :nodoc: public func send(_ error: Error, deadline: Deadline) throws { try channel.send(error, deadline: deadline) } - + /// :nodoc: public func done() { channel.done() } } - + /// Receive-only reference to an existing channel. /// /// ## Example: @@ -187,16 +187,16 @@ public final class Channel { /// ``` public final class Receiving { private let channel: Channel - + fileprivate init(_ channel: Channel) { self.channel = channel } - + /// :nodoc: @discardableResult public func receive(deadline: Deadline) throws -> Type { return try channel.receive(deadline: deadline) } - + /// :nodoc: public func done() { channel.done() @@ -222,7 +222,7 @@ class Node { var value: T var next: Node? weak var previous: Node? - + init(value: T) { self.value = value } @@ -231,48 +231,48 @@ class Node { fileprivate class List { private var head: Node? private var tail: Node? - + @discardableResult fileprivate func append(_ value: T) -> Node { let newNode = Node(value: value) - + if let tailNode = tail { newNode.previous = tailNode tailNode.next = newNode } else { head = newNode } - + tail = newNode return newNode } - + @discardableResult fileprivate func remove(_ node: Node) -> T { let prev = node.previous let next = node.next - + if let prev = prev { prev.next = next } else { head = next } - + next?.previous = prev - + if next == nil { tail = prev } - + node.previous = nil node.next = nil - + return node.value } - + @discardableResult fileprivate func removeFirst() throws -> T { guard let head = head else { throw VeniceError.unexpectedError } - + return remove(head) } } diff --git a/Sources/Venice/Coroutine.swift b/Sources/Venice/Coroutine.swift index 9ab6e48..846acaf 100644 --- a/Sources/Venice/Coroutine.swift +++ b/Sources/Venice/Coroutine.swift @@ -40,7 +40,7 @@ import CLibdill public final class Coroutine { private typealias Handle = Int32 private let handle: Handle - + /// Launches a coroutine that executes the closure passed as argument. /// The coroutine is executed concurrently, and its lifetime may exceed the lifetime /// of the caller. @@ -91,11 +91,11 @@ public final class Coroutine { handle = result } - + deinit { cancel() } - + /// Cancels the coroutine. /// /// - Warning: @@ -104,8 +104,8 @@ public final class Coroutine { public func cancel() { hclose(handle) } - - /// Explicitly passes control to other coroutines. + + /// Explicitly passes control to other coroutines. /// By calling this function, you give other coroutines a chance to run. /// /// You should consider using `Coroutiner.yield()` when doing lengthy computations @@ -129,7 +129,7 @@ public final class Coroutine { /// Thrown when the operation is performed within a canceled coroutine. public static func yield() throws { let result = CLibdill.yield() - + guard result == 0 else { switch errno { case ECANCELED: @@ -164,7 +164,7 @@ public final class Coroutine { /// Thrown when the operation is performed within a canceled coroutine. public static func wakeUp(_ deadline: Deadline) throws { let result = msleep(deadline.value) - + guard result == 0 else { switch errno { case ECANCELED: @@ -174,7 +174,7 @@ public final class Coroutine { } } } - + /// Coroutine groups are useful for canceling multiple coroutines at the /// same time. /// @@ -196,21 +196,21 @@ public final class Coroutine { public class Group { private var coroutines: [Int: Coroutine] private var finishedCoroutines: Set = [] - + private static var id = 0 - + private static func getNextID() -> Int { defer { if id == Int.max { id = -1 } - + id += 1 } - + return id } - + /// Creates a new, empty coroutine group with at least the specified number /// of elements' worth of buffer. /// @@ -242,11 +242,11 @@ public final class Coroutine { public init(minimumCapacity: Int = 0) { coroutines = [Int: Coroutine](minimumCapacity: minimumCapacity) } - + deinit { cancel() } - + /// Creates a lightweight coroutine and adds it to the group. /// /// ## Example: @@ -268,26 +268,26 @@ public final class Coroutine { /// - Returns: Newly created coroutine @discardableResult public func addCoroutine(body: @escaping () throws -> Void) throws -> Coroutine { removeFinishedCoroutines() - + var finished = false let id = Group.getNextID() - + let coroutine = try Coroutine { [unowned self] in defer { finished = true self.finishedCoroutines.insert(id) } - + try body() } - + if !finished { coroutines[id] = coroutine } - + return coroutine } - + /// Cancels all coroutines in the group. /// /// - Warning: @@ -295,21 +295,21 @@ public final class Coroutine { /// will throw `VeniceError.canceledCoroutine`. public func cancel() { removeFinishedCoroutines() - + for (id, coroutine) in coroutines { defer { coroutines[id] = nil } - + coroutine.cancel() } } - + private func removeFinishedCoroutines() { for id in finishedCoroutines { coroutines[id] = nil } - + finishedCoroutines.removeAll() } } diff --git a/Sources/Venice/Error.swift b/Sources/Venice/Error.swift index ab81f1c..0ffd5c0 100644 --- a/Sources/Venice/Error.swift +++ b/Sources/Venice/Error.swift @@ -16,7 +16,7 @@ public enum VeniceError : Error, Equatable { case readFailed /// Thrown when a write operation fails. case writeFailed - + /// Thrown when an unexpected error occurs. /// This should never happen in the regular flow of an application. case unexpectedError diff --git a/Sources/Venice/FileDescriptor.swift b/Sources/Venice/FileDescriptor.swift index 5964a7d..8667852 100644 --- a/Sources/Venice/FileDescriptor.swift +++ b/Sources/Venice/FileDescriptor.swift @@ -11,16 +11,16 @@ import CLibdill public final class FileDescriptor { /// File descriptor handle. public typealias Handle = Int32 - + /// File descriptor handle. public private(set) var handle: Handle - + /// Standard input file descriptor public static var standardInput = try! FileDescriptor(STDIN_FILENO) - + /// Standard output file descriptor public static var standardOutput = try! FileDescriptor(STDOUT_FILENO) - + /// Standard error file descriptor public static var standardError = try! FileDescriptor(STDERR_FILENO) @@ -44,11 +44,11 @@ public final class FileDescriptor { let _ = fcntl(handle, F_SETFL, flags | O_NONBLOCK) self.handle = handle } - + deinit { try? close() } - + /// Reads from the file descriptor. /// /// - Parameters: @@ -70,18 +70,18 @@ public final class FileDescriptor { deadline: Deadline ) throws -> UnsafeRawBufferPointer { let handle = try getHandle() - + guard !buffer.isEmpty, let baseAddress = buffer.baseAddress else { return UnsafeRawBufferPointer(start: nil, count: 0) } - + loop: while true { #if os(Linux) let result = Glibc.read(handle, buffer.baseAddress, buffer.count) #else let result = Darwin.read(handle, buffer.baseAddress, buffer.count) #endif - + guard result != -1 else { switch errno { case EWOULDBLOCK, EAGAIN: @@ -91,11 +91,11 @@ public final class FileDescriptor { throw VeniceError.readFailed } } - + return UnsafeRawBufferPointer(start: baseAddress, count: result) } } - + /// Writes to the file descriptor. /// /// - Parameters: @@ -120,7 +120,7 @@ public final class FileDescriptor { #else let result = Darwin.write(handle, buffer.baseAddress, buffer.count) #endif - + guard result != -1 else { switch errno { case EWOULDBLOCK, EAGAIN: @@ -130,7 +130,7 @@ public final class FileDescriptor { throw VeniceError.writeFailed } } - + #if swift(>=3.2) buffer = UnsafeRawBufferPointer(rebasing: buffer.suffix(from: result)) #else @@ -138,7 +138,7 @@ public final class FileDescriptor { #endif } } - + /// Closes a file descriptor, so that it no longer refers to any /// file and may be reused. Any record locks held on the /// file it was associated with, and owned by the process, are removed @@ -156,7 +156,7 @@ public final class FileDescriptor { /// Thrown when `handle` is not an open file descriptor. public func close() throws { let handle = try detach() - + #if os(Linux) guard Glibc.close(handle) == 0 else { throw VeniceError.invalidFileDescriptor @@ -167,30 +167,30 @@ public final class FileDescriptor { } #endif } - + /// Detaches the underlying `handle`. /// After `detach` any operation on the `FileDescriptor` will throw an error. /// /// - Returns: The underlying file descriptor. @discardableResult public func detach() throws -> Handle { let handle = try getHandle() - + defer { self.handle = -1 } - + FileDescriptor.clean(handle) return handle } - + private func getHandle() throws -> Handle { guard handle != -1 else { throw VeniceError.invalidFileDescriptor } - + return handle } - + /// Waits for the file descriptor to become either readable/writable /// or to get into an error state. Either case leads to a successful return /// from the function. To distinguish the two outcomes, follow up with a @@ -216,14 +216,14 @@ public final class FileDescriptor { /// Thrown when the operation reaches the deadline. public static func poll(_ handle: Handle, event: PollEvent, deadline: Deadline) throws { let result: Int32 - + switch event { case .read: result = fdin(handle, deadline.value) case .write: result = fdout(handle, deadline.value) } - + guard result == 0 else { switch errno { case EBADF: @@ -239,7 +239,7 @@ public final class FileDescriptor { } } } - + /// Erases cached info about a file descriptor. /// /// This function drops any state that Venice associates with @@ -253,10 +253,10 @@ public final class FileDescriptor { guard handle != -1 else { return } - + fdclean(handle) } - + /// Event used to poll file descriptors for reading or writing. public enum PollEvent { /// Event which represents when data is available diff --git a/Sources/Venice/Time.swift b/Sources/Venice/Time.swift index 0acb98d..d00ef0a 100644 --- a/Sources/Venice/Time.swift +++ b/Sources/Venice/Time.swift @@ -26,11 +26,11 @@ import CLibdill /// ``` public struct Duration { let value: Int64 - + fileprivate init(_ duration: Int) { self.value = Int64(duration) } - + /// Creates a `Deadline` from the duration. public func fromNow() -> Deadline { return Deadline(value + CLibdill.now()) @@ -56,7 +56,7 @@ extension Duration : Equatable { public struct Deadline { /// Raw value representing the deadline. public let value: Int64 - + init(_ deadline: Int64) { self.value = deadline } @@ -65,7 +65,7 @@ public struct Deadline { public static func now() -> Deadline { return Deadline(CLibdill.now()) } - + /// Special value to be used if the operation needs to be performed without blocking. public static var immediately: Deadline { return Deadline(0) @@ -82,7 +82,7 @@ extension Int { public var millisecond: Duration { return Duration(self) } - + /// `Duration` represented in milliseconds. public var milliseconds: Duration { return millisecond diff --git a/Tests/VeniceTests/Venice/CoroutineTests.swift b/Tests/VeniceTests/Venice/CoroutineTests.swift index 5db9b01..7a9d47b 100644 --- a/Tests/VeniceTests/Venice/CoroutineTests.swift +++ b/Tests/VeniceTests/Venice/CoroutineTests.swift @@ -107,21 +107,21 @@ public class CoroutineTests : XCTestCase { group.cancel() } - + func testReadWriteFileDescriptor() throws { let deadline = 1.second.fromNow() let (socket1, socket2) = try createSocketPair() - + let socket1Buffer = UnsafeMutableRawBufferPointer.allocate(count: 1) let socket2Buffer = UnsafeMutableRawBufferPointer.allocate(count: 1) - + defer { socket1Buffer.deallocate() socket2Buffer.deallocate() } - + var read: UnsafeRawBufferPointer - + socket1Buffer[0] = 42 socket2Buffer[0] = 0 try socket1.write(UnsafeRawBufferPointer(socket1Buffer), deadline: deadline) @@ -129,7 +129,7 @@ public class CoroutineTests : XCTestCase { XCTAssertEqual(read[0], 42) XCTAssertEqual(socket1Buffer[0], 42) XCTAssertEqual(socket2Buffer[0], 42) - + socket1Buffer[0] = 0 socket2Buffer[0] = 69 try socket2.write(UnsafeRawBufferPointer(socket2Buffer), deadline: deadline) @@ -176,34 +176,34 @@ public class CoroutineTests : XCTestCase { coroutine1.cancel() coroutine2.cancel() } - + func testDetachFileDescriptor() throws { var sockets = [Int32](repeating: 0, count: 2) - + #if os(Linux) let result = socketpair(AF_UNIX, Int32(SOCK_STREAM.rawValue), 0, &sockets) #else let result = socketpair(AF_UNIX, SOCK_STREAM, 0, &sockets) #endif - + XCTAssert(result == 0) - + let fileDescriptor = try FileDescriptor(sockets[0]) let socket = try fileDescriptor.detach() XCTAssertEqual(socket, sockets[0]) XCTAssertEqual(fileDescriptor.handle, -1) - + XCTAssertThrowsError( try FileDescriptor.poll(fileDescriptor.handle, event: .read, deadline: .never), error: VeniceError.invalidFileDescriptor ) } - + func testStandardStreams() { let input = FileDescriptor.standardInput let output = FileDescriptor.standardOutput let error = FileDescriptor.standardError - + XCTAssertEqual(try input.detach(), STDIN_FILENO) XCTAssertEqual(try output.detach(), STDOUT_FILENO) XCTAssertEqual(try error.detach(), STDERR_FILENO)