1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
internal import class Dispatch.DispatchQueue
@preconcurrency internal import struct SystemPackage.FileDescriptor
internal import struct SystemPackage.FilePath
/// A write-only thread-safe handle to an open file.
package actor OpenWritableFile: WritableStream {
/// Underlying storage for this file handle, dependent on the file system type that produced it.
enum Storage {
/// Operating system file descriptor and a queue used for reading from that file descriptor without blocking
/// the Swift Concurrency thread pool.
case real(FileDescriptor, DispatchQueue)
/// Reference to the ``MockFileSystem`` actor that provides file storage.
case mock(MockFileSystem)
}
/// Concrete instance of underlying storage.
let storage: Storage
/// Absolute path to the file represented by this file handle.
let path: FilePath
/// Whether the underlying file descriptor has been closed.
private var isClosed = false
/// Creates a new write-only file handle.
/// - Parameters:
/// - storage: Underlying storage for the file.
/// - path: Absolute path to the file on the file system that provides `storage`.
init(storage: OpenWritableFile.Storage, path: FilePath) {
self.storage = storage
self.path = path
}
/// Writes a sequence of bytes to the buffer.
package func write(_ bytes: some Collection<UInt8> & Sendable) async throws {
assert(!isClosed)
switch self.storage {
case let .real(fileDescriptor, queue):
let path = self.path
try await queue.scheduleOnQueue {
do {
let writtenBytesCount = try fileDescriptor.writeAll(bytes)
assert(bytes.count == writtenBytesCount)
} catch {
throw error.attach(path)
}
}
case let .mock(storage):
await storage.write(path: self.path, bytes: bytes)
}
}
/// Closes the underlying stream handle. It is a programmer error to write to a stream after it's closed.
package func close() async throws {
isClosed = true
guard case let .real(fileDescriptor, queue) = self.storage else {
return
}
try await queue.scheduleOnQueue {
try fileDescriptor.close()
}
}
}
|