Skip to content

Commit 77feb1c

Browse files
committed
[wasm] Make Data.WritingOptions.atomic unavailable on WASI
`writeToFileAux`, `createTemporaryFile`, and `createProtectedTemporaryFile` also become unavailable on WASI.
1 parent 22500b8 commit 77feb1c

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
@@ -136,6 +136,9 @@ private func cleanupTemporaryDirectory(at inPath: String?) {
136136
}
137137

138138
/// Caller is responsible for calling `close` on the `Int32` file descriptor.
139+
#if os(WASI)
140+
@available(*, unavailable, message: "WASI does not have temporary directories")
141+
#endif
139142
private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, prefix: String, options: Data.WritingOptions, variant: String? = nil) throws -> (Int32, String) {
140143
#if os(WASI)
141144
// WASI does not have temp directories
@@ -200,7 +203,14 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
200203

201204
/// Returns `(file descriptor, temporary file path, temporary directory path)`
202205
/// 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.
206+
#if os(WASI)
207+
@available(*, unavailable, message: "WASI does not have temporary directories")
208+
#endif
203209
private func createProtectedTemporaryFile(at destinationPath: String, inPath: PathOrURL, options: Data.WritingOptions, variant: String? = nil) throws -> (Int32, String, String?) {
210+
#if os(WASI)
211+
// WASI does not have temp directories
212+
throw CocoaError(.featureUnsupported)
213+
#else
204214
#if FOUNDATION_FRAMEWORK
205215
if _foundation_sandbox_check(getpid(), nil) != 0 {
206216
// Convert the path back into a string
@@ -240,6 +250,7 @@ private func createProtectedTemporaryFile(at destinationPath: String, inPath: Pa
240250
let temporaryDirectoryPath = destinationPath.deletingLastPathComponent()
241251
let (fd, auxFile) = try createTemporaryFile(at: temporaryDirectoryPath, inPath: inPath, prefix: ".dat.nosync", options: options, variant: variant)
242252
return (fd, auxFile, nil)
253+
#endif // os(WASI)
243254
}
244255

245256
private func write(buffer: UnsafeRawBufferPointer, toFileDescriptor fd: Int32, path: PathOrURL, parentProgress: Progress?) throws {
@@ -314,15 +325,26 @@ internal func writeToFile(path inPath: PathOrURL, data: Data, options: Data.Writ
314325
}
315326

316327
internal func writeToFile(path inPath: PathOrURL, buffer: UnsafeRawBufferPointer, options: Data.WritingOptions, attributes: [String : Data] = [:], reportProgress: Bool = false) throws {
328+
#if os(WASI) // `.atomic` is unavailable on WASI
329+
try writeToFileNoAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
330+
#else
317331
if options.contains(.atomic) {
318332
try writeToFileAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
319333
} else {
320334
try writeToFileNoAux(path: inPath, buffer: buffer, options: options, attributes: attributes, reportProgress: reportProgress)
321335
}
336+
#endif
322337
}
323338

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

328350
// TODO: Somehow avoid copying back and forth to a String to hold the path
@@ -495,7 +517,6 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
495517

496518
cleanupTemporaryDirectory(at: temporaryDirectoryPath)
497519

498-
#if !os(WASI) // WASI does not support fchmod for now
499520
if let mode {
500521
// Try to change the mode if the path has not changed. Do our best, but don't report an error.
501522
#if FOUNDATION_FRAMEWORK
@@ -519,16 +540,18 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
519540
fchmod(fd, mode)
520541
#endif
521542
}
522-
#endif // os(WASI)
523543
}
524544
}
525545
}
526546
#endif
547+
#endif // os(WASI)
527548
}
528549

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

533556
#if os(Windows)
534557
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
@@ -2057,6 +2057,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
20572057
public init(rawValue: UInt) { self.rawValue = rawValue }
20582058

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

20622065
/// An option that attempts to write data to a file and fails with an error if the destination file already exists.
@@ -2442,9 +2445,11 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
24422445
/// - parameter options: Options for writing the data. Default value is `[]`.
24432446
/// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`.
24442447
public func write(to url: URL, options: Data.WritingOptions = []) throws {
2448+
#if !os(WASI) // `.atomic` is unavailable on WASI
24452449
if options.contains(.withoutOverwriting) && options.contains(.atomic) {
24462450
fatalError("withoutOverwriting is not supported with atomic")
24472451
}
2452+
#endif
24482453

24492454
guard url.isFileURL else {
24502455
throw CocoaError(.fileWriteUnsupportedScheme)

Sources/FoundationEssentials/FileManager/FileManager+Files.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ extension _FileManagerImpl {
277277
attr?[.protectionKey] = nil
278278
}
279279
#elseif os(WASI)
280-
// WASI doesn't support a temporary file, so can't use `.atomic`
280+
// `.atomic` is unavailable on WASI
281281
let opts: Data.WritingOptions = []
282282
#else
283283
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)