Skip to content

Commit 5fa6a09

Browse files
committed
[wasm] Make Data.WritingOptions.atomic unavailable on WASI
`writeToFileAux`, `createTemporaryFile`, and `createProtectedTemporaryFile` also become unavailable on WASI.
1 parent 97b147b commit 5fa6a09

File tree

6 files changed

+49
-3
lines changed

6 files changed

+49
-3
lines changed

Benchmarks/Benchmarks/DataIO/BenchmarkDataIO.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ let benchmarks = {
8888
try data.write(to: testPath)
8989
}
9090

91+
#if !os(WASI) // atomic writing is unavailable on WASI
9192
Benchmark("write-regularFile-atomic", configuration: .cleanupTestPathConfig) { benchmark in
9293
try data.write(to: testPath, options: .atomic)
9394
}
95+
#endif
9496

9597
Benchmark("write-regularFile-alreadyExists",
9698
configuration: .init(
@@ -103,6 +105,7 @@ let benchmarks = {
103105
try? data.write(to: testPath)
104106
}
105107

108+
#if !os(WASI) // atomic writing is unavailable on WASI
106109
Benchmark("write-regularFile-alreadyExists-atomic",
107110
configuration: .init(
108111
setup: {
@@ -113,6 +116,7 @@ let benchmarks = {
113116
) { benchmark in
114117
try? data.write(to: testPath, options: .atomic)
115118
}
119+
#endif
116120

117121
Benchmark("read-regularFile",
118122
configuration: .init(

Sources/FoundationEssentials/Data/Data+Writing.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ private func cleanupTemporaryDirectory(at inPath: String?) {
140140
}
141141

142142
/// Caller is responsible for calling `close` on the `Int32` file descriptor.
143+
#if os(WASI)
144+
@available(*, unavailable, message: "WASI does not have temporary directories")
145+
#endif
143146
private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, prefix: String, options: Data.WritingOptions, variant: String? = nil) throws -> (Int32, String) {
144147
#if os(WASI)
145148
// WASI does not have temp directories
@@ -204,7 +207,14 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
204207

205208
/// Returns `(file descriptor, temporary file path, temporary directory path)`
206209
/// Caller is responsible for calling `close` on the `Int32` file descriptor and calling `cleanupTemporaryDirectory` on the temporary directory path. The temporary directory path may be nil, if it does not need to be cleaned up.
210+
#if os(WASI)
211+
@available(*, unavailable, message: "WASI does not have temporary directories")
212+
#endif
207213
private func createProtectedTemporaryFile(at destinationPath: String, inPath: PathOrURL, options: Data.WritingOptions, variant: String? = nil) throws -> (Int32, String, String?) {
214+
#if os(WASI)
215+
// WASI does not have temp directories
216+
throw CocoaError(.featureUnsupported)
217+
#else
208218
#if FOUNDATION_FRAMEWORK
209219
if _foundation_sandbox_check(getpid(), nil) != 0 {
210220
// Convert the path back into a string
@@ -244,6 +254,7 @@ private func createProtectedTemporaryFile(at destinationPath: String, inPath: Pa
244254
let temporaryDirectoryPath = destinationPath.deletingLastPathComponent()
245255
let (fd, auxFile) = try createTemporaryFile(at: temporaryDirectoryPath, inPath: inPath, prefix: ".dat.nosync", options: options, variant: variant)
246256
return (fd, auxFile, nil)
257+
#endif // os(WASI)
247258
}
248259

249260
private func write(buffer: UnsafeRawBufferPointer, toFileDescriptor fd: Int32, path: PathOrURL, parentProgress: Progress?) throws {
@@ -318,15 +329,26 @@ internal func writeToFile(path inPath: PathOrURL, data: Data, options: Data.Writ
318329
}
319330

320331
internal func writeToFile(path inPath: PathOrURL, buffer: UnsafeRawBufferPointer, options: Data.WritingOptions, attributes: [String : Data] = [:], reportProgress: Bool = false) throws {
332+
#if os(WASI) // `.atomic` is unavailable on WASI
333+
try writeToFileNoAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
334+
#else
321335
if options.contains(.atomic) {
322336
try writeToFileAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
323337
} else {
324338
try writeToFileNoAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
325339
}
340+
#endif
326341
}
327342

328343
/// Create a new file out of `Data` at a path, using atomic writing.
344+
#if os(WASI)
345+
@available(*, unavailable, message: "atomic writing is unavailable in WASI because temporary files are not supported")
346+
#endif
329347
private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPointer, options: Data.WritingOptions, attributes: [String : Data], reportProgress: Bool) throws {
348+
#if os(WASI)
349+
// `.atomic` is unavailable on WASI
350+
throw CocoaError(.featureUnsupported)
351+
#else
330352
assert(options.contains(.atomic))
331353

332354
// TODO: Somehow avoid copying back and forth to a String to hold the path
@@ -499,7 +521,6 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
499521

500522
cleanupTemporaryDirectory(at: temporaryDirectoryPath)
501523

502-
#if !os(WASI) // WASI does not support fchmod for now
503524
if let mode {
504525
// Try to change the mode if the path has not changed. Do our best, but don't report an error.
505526
#if FOUNDATION_FRAMEWORK
@@ -523,16 +544,18 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
523544
fchmod(fd, mode)
524545
#endif
525546
}
526-
#endif // os(WASI)
527547
}
528548
}
529549
}
530550
#endif
551+
#endif // os(WASI)
531552
}
532553

533554
/// Create a new file out of `Data` at a path, not using atomic writing.
534555
private func writeToFileNoAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPointer, options: Data.WritingOptions, attributes: [String : Data], reportProgress: Bool) throws {
556+
#if !os(WASI) // `.atomic` is unavailable on WASI
535557
assert(!options.contains(.atomic))
558+
#endif
536559

537560
#if os(Windows)
538561
try inPath.path.withNTPathRepresentation { pwszPath in

Sources/FoundationEssentials/Data/Data.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
20992099
public init(rawValue: UInt) { self.rawValue = rawValue }
21002100

21012101
/// An option to write data to an auxiliary file first and then replace the original file with the auxiliary file when the write completes.
2102+
#if os(WASI)
2103+
@available(*, unavailable, message: "atomic writing is unavailable in WASI because temporary files are not supported")
2104+
#endif
21022105
public static let atomic = WritingOptions(rawValue: 1 << 0)
21032106

21042107
/// An option that attempts to write data to a file and fails with an error if the destination file already exists.
@@ -2484,9 +2487,11 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
24842487
/// - parameter options: Options for writing the data. Default value is `[]`.
24852488
/// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`.
24862489
public func write(to url: URL, options: Data.WritingOptions = []) throws {
2490+
#if !os(WASI) // `.atomic` is unavailable on WASI
24872491
if options.contains(.withoutOverwriting) && options.contains(.atomic) {
24882492
fatalError("withoutOverwriting is not supported with atomic")
24892493
}
2494+
#endif
24902495

24912496
guard url.isFileURL else {
24922497
throw CocoaError(.fileWriteUnsupportedScheme)

Sources/FoundationEssentials/FileManager/FileManager+Files.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ extension _FileManagerImpl {
261261
attr?[.protectionKey] = nil
262262
}
263263
#elseif os(WASI)
264-
// WASI doesn't support a temporary file, so can't use `.atomic`
264+
// `.atomic` is unavailable on WASI
265265
let opts: Data.WritingOptions = []
266266
#else
267267
let opts = Data.WritingOptions.atomic

Sources/FoundationEssentials/String/String+IO.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,12 @@ extension StringProtocol {
447447
attributes = [:]
448448
}
449449

450+
#if os(WASI)
451+
guard !useAuxiliaryFile else { throw CocoaError(.featureUnsupported) }
452+
let options : Data.WritingOptions = []
453+
#else
450454
let options : Data.WritingOptions = useAuxiliaryFile ? [.atomic] : []
455+
#endif
451456

452457
try writeToFile(path: .path(String(path)), data: data, options: options, attributes: attributes, reportProgress: false)
453458
}
@@ -465,7 +470,12 @@ extension StringProtocol {
465470
attributes = [:]
466471
}
467472

473+
#if os(WASI)
474+
guard !useAuxiliaryFile else { throw CocoaError(.featureUnsupported) }
475+
let options : Data.WritingOptions = []
476+
#else
468477
let options : Data.WritingOptions = useAuxiliaryFile ? [.atomic] : []
478+
#endif
469479

470480
try writeToFile(path: .url(url), data: data, options: options, attributes: attributes, reportProgress: false)
471481
}

Tests/FoundationEssentialsTests/DataIOTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class DataIOTests : XCTestCase {
9393

9494
// Atomic writing is a very different code path
9595
func test_readWriteAtomic() throws {
96+
#if os(WASI)
97+
try XCTSkip("atomic writing is not supported on WASI")
98+
#else
9699
let url = testURL()
97100
// Perform an atomic write to a file that does not exist
98101
try writeAndVerifyTestData(to: url, writeOptions: [.atomic])
@@ -101,6 +104,7 @@ class DataIOTests : XCTestCase {
101104
try writeAndVerifyTestData(to: url, writeOptions: [.atomic])
102105

103106
cleanup(at: url)
107+
#endif
104108
}
105109

106110
func test_readWriteMapped() throws {

0 commit comments

Comments
 (0)