mirror of
https://github.com/apple/swift-nio.git
synced 2026-05-20 20:30:36 +00:00
Motivation:
`IOData` is a legacy but alas also core type that needs to be
`Sendable`. Before this PR however it can't be `Sendable` because it
holds a `FileRegion` which holds a `NIOFileDescriptor`. So let's make
all of these `Sendable` but let's also start the deprecation journey for
the following types:
- `IOData`, now soft-deprecated (no warnings) because on its reliance on
`FileRegion`
- `FileRegion`, now soft-deprecated (no warnings) because on its
reliance on `NIOFileHandle`
- `NIOFileHandle`, now soft-deprecated (warnings on the
`NIOFileHandle(descriptor:)` constructor but with a
`NIOFileHandle(_deprecatedTakingOwnershipOfDescriptor:)` alternative
- `NonBlockingFileIO`, now soft-deprecated (warnings on the `openFile`
functions (but with `_deprecated` alternatives) because of their
reliance on `NIOFileHandle)
Modification:
- Make `NIOFileDescriptor`, `FileRegion` and `IOData` `Sendable` by
tracking the fd number and the usage state in an atomic
- Enforce singular access by making the `withFileDescriptor { fd ... }`
function atomically exchange the fd number for a "I'm busy" sentinel
value
- Start deprecating `IOData`, `NIOFileHandle`, `NonBlockingFileIO`,
`FileRegion`
Result:
- `NIOFileDescriptor`, `FileRegion` and `IOData` can be `Sendable`
123 lines
4.6 KiB
Swift
123 lines
4.6 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2017-2024 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import XCTest
|
|
|
|
@testable import NIOCore
|
|
|
|
class BaseObjectTest: XCTestCase {
|
|
func testNIOByteBufferConversion() {
|
|
let expected = ByteBufferAllocator().buffer(capacity: 1024)
|
|
let asAny = NIOAny(expected)
|
|
XCTAssertEqual(expected, asAny.forceAs(type: ByteBuffer.self))
|
|
XCTAssertEqual(expected, asAny.forceAsByteBuffer())
|
|
if let actual = asAny.tryAs(type: ByteBuffer.self) {
|
|
XCTAssertEqual(expected, actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
if let actual = asAny.tryAsByteBuffer() {
|
|
XCTAssertEqual(expected, actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
}
|
|
|
|
func testNIOIODataConversion() {
|
|
let expected = IOData.byteBuffer(ByteBufferAllocator().buffer(capacity: 1024))
|
|
let asAny = NIOAny(expected)
|
|
XCTAssertEqual(expected, asAny.forceAs(type: IOData.self))
|
|
XCTAssertEqual(expected, asAny.forceAsIOData())
|
|
if let actual = asAny.tryAs(type: IOData.self) {
|
|
XCTAssertEqual(expected, actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
if let actual = asAny.tryAsIOData() {
|
|
XCTAssertEqual(expected, actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
}
|
|
|
|
func testNIOFileRegionConversion() {
|
|
let handle = NIOFileHandle(_deprecatedTakingOwnershipOfDescriptor: -1)
|
|
let expected = FileRegion(fileHandle: handle, readerIndex: 1, endIndex: 2)
|
|
defer {
|
|
// fake descriptor, so shouldn't be closed.
|
|
XCTAssertNoThrow(try handle.takeDescriptorOwnership())
|
|
}
|
|
let asAny = NIOAny(expected)
|
|
XCTAssert(expected == asAny.forceAs(type: FileRegion.self))
|
|
XCTAssert(expected == asAny.forceAsFileRegion())
|
|
if let actual = asAny.tryAs(type: FileRegion.self) {
|
|
XCTAssert(expected == actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
if let actual = asAny.tryAsFileRegion() {
|
|
XCTAssert(expected == actual)
|
|
} else {
|
|
XCTFail("tryAs didn't work")
|
|
}
|
|
}
|
|
|
|
func testBadConversions() {
|
|
let handle = NIOFileHandle(_deprecatedTakingOwnershipOfDescriptor: -1)
|
|
let bb = ByteBufferAllocator().buffer(capacity: 1024)
|
|
let fr = FileRegion(fileHandle: handle, readerIndex: 1, endIndex: 2)
|
|
defer {
|
|
// fake descriptor, so shouldn't be closed.
|
|
XCTAssertNoThrow(try handle.takeDescriptorOwnership())
|
|
}
|
|
let id = IOData.byteBuffer(bb)
|
|
|
|
XCTAssertNil(NIOAny(bb).tryAsFileRegion())
|
|
XCTAssertNil(NIOAny(fr).tryAsByteBuffer())
|
|
XCTAssertNil(NIOAny(id).tryAsFileRegion())
|
|
}
|
|
|
|
func testByteBufferFromIOData() {
|
|
let expected = ByteBufferAllocator().buffer(capacity: 1024)
|
|
let wrapped = IOData.byteBuffer(expected)
|
|
XCTAssertEqual(expected, NIOAny(wrapped).tryAsByteBuffer())
|
|
}
|
|
|
|
func testFileRegionFromIOData() {
|
|
let handle = NIOFileHandle(_deprecatedTakingOwnershipOfDescriptor: -1)
|
|
let expected = FileRegion(fileHandle: handle, readerIndex: 1, endIndex: 2)
|
|
defer {
|
|
// fake descriptor, so shouldn't be closed.
|
|
XCTAssertNoThrow(try handle.takeDescriptorOwnership())
|
|
}
|
|
let wrapped = IOData.fileRegion(expected)
|
|
XCTAssert(expected == NIOAny(wrapped).tryAsFileRegion())
|
|
}
|
|
|
|
func testIODataEquals() {
|
|
let handle = NIOFileHandle(_deprecatedTakingOwnershipOfDescriptor: -1)
|
|
var bb1 = ByteBufferAllocator().buffer(capacity: 1024)
|
|
let bb2 = ByteBufferAllocator().buffer(capacity: 1024)
|
|
bb1.writeString("hello")
|
|
let fr = FileRegion(fileHandle: handle, readerIndex: 1, endIndex: 2)
|
|
defer {
|
|
// fake descriptor, so shouldn't be closed.
|
|
XCTAssertNoThrow(try handle.takeDescriptorOwnership())
|
|
}
|
|
XCTAssertEqual(IOData.byteBuffer(bb1), IOData.byteBuffer(bb1))
|
|
XCTAssertNotEqual(IOData.byteBuffer(bb1), IOData.byteBuffer(bb2))
|
|
XCTAssertNotEqual(IOData.byteBuffer(bb1), IOData.fileRegion(fr))
|
|
}
|
|
}
|