Skip to content

Commit

Permalink
Lift non-blocking requirements for FileDescriptor (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidask authored May 20, 2017
1 parent adeaae8 commit e81c3f7
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 30 deletions.
48 changes: 25 additions & 23 deletions Sources/Venice/FileDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@ import CLibdill
/// such as a pipe or network socket.
public final class FileDescriptor {
/// File descriptor handle.
public private(set) var fileDescriptor: Int32

/// Creates a `FileDescriptor` from a file descriptor handle and
/// configures it as non-blocking.
public private(set) var handle: Int32

/// Creates a `FileDescriptor` from a file descriptor handle
///
/// - Parameters:
/// - fileDescriptor: Previously opened file descriptor.
public init(handle: Int32) {
self.handle = handle
}

/// Configures the `FileDescriptor` to non-blocking
///
/// - Throws: The following errors might be thrown:
/// #### VeniceError.invalidFileDescriptor
/// Thrown when `fileDescriptor` is not an open file descriptor.
public init(_ fileDescriptor: Int32) throws {
let flags = fcntl(fileDescriptor, F_GETFL, 0)
/// Thrown when `handle` is not an open file descriptor.
public func setNonblocking() throws {
let flags = fcntl(handle, F_GETFL, 0)

guard flags != -1 else {
throw VeniceError.invalidFileDescriptor
}
guard fcntl(fileDescriptor, F_SETFL, flags | O_NONBLOCK) == 0 else {

guard fcntl(handle, F_SETFL, flags | O_NONBLOCK) == 0 else {
throw VeniceError.invalidFileDescriptor
}

self.fileDescriptor = fileDescriptor
}

deinit {
Expand Down Expand Up @@ -70,9 +72,9 @@ public final class FileDescriptor {

switch event {
case .read:
result = fdin(fileDescriptor, deadline.value)
result = fdin(handle, deadline.value)
case .write:
result = fdout(fileDescriptor, deadline.value)
result = fdout(handle, deadline.value)
}

guard result == 0 else {
Expand Down Expand Up @@ -101,11 +103,11 @@ public final class FileDescriptor {
/// third-party libraries, just before returning them back to
/// their original owners. Otherwise the behavior is **undefined**.
public func clean() {
guard fileDescriptor != -1 else {
guard handle != -1 else {
return
}

fdclean(fileDescriptor)
fdclean(handle)
}

/// Closes a file descriptor, so that it no longer refers to any
Expand All @@ -114,41 +116,41 @@ public final class FileDescriptor {
/// (regardless of the file descriptor that was used to obtain the lock).
///
/// - Warning:
/// If `fileDescriptor` is the last file descriptor referring to the underlying open
/// If `handle` is the last file descriptor referring to the underlying open
/// file description, the resources associated with the
/// open file description are freed; if the file descriptor was the last
/// reference to a file which has been removed using `unlink`, the file
/// is deleted.
///
/// - Throws: The following errors might be thrown:
/// #### VeniceError.invalidFileDescriptor
/// Thrown when `fileDescriptor` is not an open file descriptor.
/// Thrown when `handle` is not an open file descriptor.
public func close() throws {
clean()

#if os(Linux)
guard Glibc.close(fileDescriptor) == 0 else {
guard Glibc.close(handle) == 0 else {
throw VeniceError.invalidFileDescriptor
}
#else
guard Darwin.close(fileDescriptor) == 0 else {
guard Darwin.close(handle) == 0 else {
throw VeniceError.invalidFileDescriptor
}
#endif
}

/// Detaches the underlying `fileDescriptor`.
/// Detaches the underlying `handle`.
/// After `detach` any operation will throw an error.
///
/// - Returns: The underlying file descriptor.
@discardableResult public func detach() -> Int32 {
clean()

defer {
fileDescriptor = -1
handle = -1
}

return fileDescriptor
return handle
}

/// Event used to poll file descriptors for reading or writing.
Expand Down
16 changes: 9 additions & 7 deletions Tests/VeniceTests/Venice/CoroutineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,24 @@ public class CoroutineTests : XCTestCase {
error: VeniceError.deadlineReached
)

var size = send(socket2.fileDescriptor, "A", 1, 0)
var size = send(socket2.handle, "A", 1, 0)
XCTAssert(size == 1)

try socket1.poll(event: .write, deadline: 100.milliseconds.fromNow())
try socket1.poll(event: .read, deadline: 100.milliseconds.fromNow())

var character: Int8 = 0
size = recv(socket1.fileDescriptor, &character, 1, 0)
size = recv(socket1.handle, &character, 1, 0)

XCTAssert(size == 1)
XCTAssert(character == 65)
}

func testInvalidFileDescriptor() throws {
let fd = FileDescriptor(handle: -1)

XCTAssertThrowsError(
try FileDescriptor(-1),
try fd.setNonblocking(),
error: VeniceError.invalidFileDescriptor
)
}
Expand Down Expand Up @@ -174,7 +176,7 @@ public class CoroutineTests : XCTestCase {
}

func testCleanFileDescriptor() throws {
let fileDescriptor = try FileDescriptor(STDIN_FILENO)
let fileDescriptor = FileDescriptor(handle: STDIN_FILENO)
fileDescriptor.clean()
}

Expand All @@ -189,10 +191,10 @@ public class CoroutineTests : XCTestCase {

XCTAssert(result == 0)

let fileDescriptor = try FileDescriptor(sockets[0])
let fileDescriptor = FileDescriptor(handle: sockets[0])
let standardInput = fileDescriptor.detach()
XCTAssertEqual(standardInput, sockets[0])
XCTAssertEqual(fileDescriptor.fileDescriptor, -1)
XCTAssertEqual(fileDescriptor.handle, -1)

XCTAssertThrowsError(
try fileDescriptor.poll(event: .read, deadline: .never),
Expand All @@ -212,7 +214,7 @@ func createSocketPair() throws -> (FileDescriptor, FileDescriptor) {

XCTAssert(result == 0)

return try (FileDescriptor(sockets[0]), FileDescriptor(sockets[1]))
return (FileDescriptor(handle: sockets[0]), FileDescriptor(handle: sockets[1]))
}

extension CoroutineTests {
Expand Down

0 comments on commit e81c3f7

Please sign in to comment.