Skip to content

Commit

Permalink
Merge commit 'fc79798d5a150d61361a27ce0c51169b889e23de'
Browse files Browse the repository at this point in the history
* commit 'fc79798d5a150d61361a27ce0c51169b889e23de':
  NIOSendableBox: allow off-loop initialisation iff Value is Sendable (apple#2753)
  Throw an appropriate error from the writer when the channel closed (apple#2744)
  put snippet code inside @available function (apple#2750)
  fix link to NIOFileSystem from NIO index page (apple#2747)
  convert the NIOFileSystem example code to a Snippet (apple#2746)
  Silence warning about missing include in macOS builds (apple#2741)
  Correctly mark 304 as not having a response body (apple#2737)
  Update availability guard (apple#2739)
  Add API for setting last accessed and last modified file times (apple#2735)
  Add a fallback path if renameat2 fails (apple#2733)
  Release file handles back to caller on failure to take ownership (apple#2715)
  Add a version of 'write' for 'ByteBuffer' (apple#2730)
  Imrprove rename error (apple#2731)
  Remove storage indirection for FileSystemError (apple#2726)
  testSimpleMPTCP should not fail for ENOPROTOOPT (apple#2725)
  Fix race in TCPThroughputBenchmark (apple#2724)
  • Loading branch information
chkp-aviads committed Jul 21, 2024
2 parents 604863b + fc79798 commit e469385
Show file tree
Hide file tree
Showing 30 changed files with 962 additions and 310 deletions.
103 changes: 103 additions & 0 deletions Snippets/NIOFileSystemTour.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// snippet.hide
import _NIOFileSystem
import NIOCore

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
func main() async throws
{
// snippet.show

// NIOFileSystem provides access to the local file system via the FileSystem
// type which is available as a global shared instance.
let fileSystem = FileSystem.shared

// Files can be inspected by using 'info':
if let info = try await fileSystem.info(forFileAt: "/Users/hal9000/demise-of-dave.txt") {
print("demise-of-dave.txt has type '\(info.type)'")
} else {
print("demise-of-dave.txt doesn't exist")
}

// Let's find out what's in that file.
do {
// Reading a whole file requires a limit. If the file is larger than the limit
// then an error is thrown. This avoids accidentally consuming too much memory
// if the file is larger than expected.
let plan = try await ByteBuffer(
contentsOf: "/Users/hal9000/demise-of-dave.txt",
maximumSizeAllowed: .mebibytes(1)
)
print("Plan for Dave's demise:", String(decoding: plan.readableBytesView, as: UTF8.self))
} catch let error as FileSystemError where error.code == .notFound {
// All errors thrown by the module have type FileSystemError (or
// Swift.CancellationError). It looks like the file doesn't exist. Let's
// create it now.
//
// The code above for reading the file is shorthand for opening the file in
// read-only mode and then reading its contents. The FileSystemProtocol
// has a few different 'withFileHandle' methods for opening a file in different
// modes. Let's open a file for writing, creating it at the same time.
try await fileSystem.withFileHandle(
forWritingAt: "/Users/hal9000/demise-of-dave.txt",
options: .newFile(replaceExisting: false)
) { file in
let plan = ByteBuffer(string: "TODO...")
try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0)
}
}

// Directories can be opened like regular files but they cannot be read from or
// written to. However, their contents can be listed:
let path: FilePath? = try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Music") { directory in
for try await entry in directory.listContents() {
if entry.name.extension == "mp3", entry.name.stem.contains("daisy") {
// Found it!
return entry.path
}
}
// No luck.
return nil
}

if let path = path {
print("Found file at '\(path)'")
}

// The file system can also be used to perform the following operations on files
// and directories:
// - copy,
// - remove,
// - rename, and
// - replace.
//
// Here's an example of copying a directory:
try await fileSystem.copyItem(at: "/Users/hal9000/Music", to: "/Volumes/Tardis/Music")

// Symbolic links can also be created (and read with 'destinationOfSymbolicLink(at:)').
try await fileSystem.createSymbolicLink(at: "/Users/hal9000/Backup", withDestination: "/Volumes/Tardis")

// Opening a symbolic link opens its destination so in most cases there's no
// need to read the destination of a symbolic link:
try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Backup") { directory in
// Beyond listing the contents of a directory, the directory handle provides a
// number of other functions, many of which are also available on regular file
// handles.
//
// This includes getting information about a file, such as its permissions, last access time,
// and last modification time:
let info = try await directory.info()
print("The directory has permissions '\(info.permissions)'")

// Where supported, the extended attributes of a file can also be accessed, read, and modified:
for attribute in try await directory.attributeNames() {
let value = try await directory.valueForAttribute(attribute)
print("Extended attribute '\(attribute)' has value '\(value)'")
}

// Once this closure returns the file system will close the directory handle freeing
// any resources required to access it such as file descriptors. Handles can also be opened
// with the 'openFile' and 'openDirectory' APIs but that places the onus you to close the
// handle at an appropriate time to avoid leaking resources.
}
// snippet.end
}
12 changes: 11 additions & 1 deletion Sources/CNIOLinux/include/CNIOLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,19 @@
#include <pthread.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include "liburing_nio.h"
#include <linux/vm_sockets.h>
#include <fcntl.h>
#include <fts.h>
#include <stdio.h>
#include <dirent.h>
#endif

// We need to include this outside the `#ifdef` so macOS builds don't warn about the missing include,
// but we also need to make sure the system includes come before it on Linux, so we put it down here
// between an `#endif/#ifdef` pair rather than at the top.
#include "liburing_nio.h"

#ifdef __linux__

#if __has_include(<linux/mptcp.h>)
#include <linux/mptcp.h>
Expand Down Expand Up @@ -133,5 +140,8 @@ extern const int CNIOLinux_AT_EMPTY_PATH;
extern const unsigned int CNIOLinux_RENAME_NOREPLACE;
extern const unsigned int CNIOLinux_RENAME_EXCHANGE;

extern const unsigned long CNIOLinux_UTIME_OMIT;
extern const unsigned long CNIOLinux_UTIME_NOW;

#endif
#endif
4 changes: 4 additions & 0 deletions Sources/CNIOLinux/shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ void CNIOLinux_i_do_nothing_just_working_around_a_darwin_toolchain_bug(void) {}
#include <assert.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>

_Static_assert(sizeof(CNIOLinux_mmsghdr) == sizeof(struct mmsghdr),
"sizes of CNIOLinux_mmsghdr and struct mmsghdr differ");
Expand Down Expand Up @@ -211,4 +212,7 @@ const unsigned int CNIOLinux_RENAME_NOREPLACE = RENAME_NOREPLACE;
const unsigned int CNIOLinux_RENAME_EXCHANGE = RENAME_EXCHANGE;
const int CNIOLinux_AT_EMPTY_PATH = AT_EMPTY_PATH;

const unsigned long CNIOLinux_UTIME_OMIT = UTIME_OMIT;
const unsigned long CNIOLinux_UTIME_NOW = UTIME_NOW;

#endif
2 changes: 1 addition & 1 deletion Sources/NIO/Docs.docc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ The core SwiftNIO repository will contain a few extremely important protocol imp
[module-tls]: ./NIOTLS
[module-websocket]: ./NIOWebSocket
[module-test-utilities]: ./NIOTestUtils
[module-filesystem]: ./NIOFileSystem
[module-filesystem]: ./_NIOFileSystem

[ch]: ./NIOCore/ChannelHandler
[c]: ./NIOCore/Channel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal final class NIOAsyncChannelOutboundWriterHandler<OutboundOut: Sendable>
@inlinable
func handlerRemoved(context: ChannelHandlerContext) {
self.context = nil
self.sink?.finish()
self.sink?.finish(error: ChannelError.ioOnClosedChannel)
self.writer = nil
}

Expand All @@ -150,7 +150,7 @@ internal final class NIOAsyncChannelOutboundWriterHandler<OutboundOut: Sendable>

@inlinable
func channelInactive(context: ChannelHandlerContext) {
self.sink?.finish()
self.sink?.finish(error: ChannelError.ioOnClosedChannel)
context.fireChannelInactive()
}

Expand Down
20 changes: 20 additions & 0 deletions Sources/NIOCore/NIOLoopBound.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,26 @@ public final class NIOLoopBoundBox<Value>: @unchecked Sendable {
return .init(_value: nil, uncheckedEventLoop: eventLoop)
}

/// Initialise a ``NIOLoopBoundBox`` by sending a `Sendable` value, validly callable off `eventLoop`.
///
/// Contrary to ``init(_:eventLoop:)``, this method can be called off `eventLoop` because we know that `value` is `Sendable`.
/// So we don't need to protect `value` itself, we just need to protect the ``NIOLoopBoundBox`` against mutations which we do because the ``value``
/// accessors are checking that we're on `eventLoop`.
public static func makeBoxSendingValue(
_ value: Value,
as: Value.Type = Value.self,
eventLoop: EventLoop
) -> NIOLoopBoundBox<Value> where Value: Sendable {
// Here, we -- possibly surprisingly -- do not precondition being on the EventLoop. This is okay for a few
// reasons:
// - This function only works with `Sendable` values, so we don't need to worry about somebody
// still holding a reference to this.
// - Because of Swift's Definitive Initialisation (DI), we know that we did write `self._value` before `init`
// returns.
// - The only way to ever write (or read indeed) `self._value` is by proving to be inside the `EventLoop`.
return .init(_value: value, uncheckedEventLoop: eventLoop)
}

/// Access the `value` with the precondition that the code is running on `eventLoop`.
///
/// - note: ``NIOLoopBoundBox`` itself is reference-typed, so any writes will affect anybody sharing this reference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

### Write bytes to a file

- ``write(contentsOf:toAbsoluteOffset:)``
- ``write(contentsOf:toAbsoluteOffset:)-57fnc``
- ``write(contentsOf:toAbsoluteOffset:)-4na03``
- ``bufferedWriter(startingAtAbsoluteOffset:capacity:)``

### Resize a file
Expand Down
174 changes: 0 additions & 174 deletions Sources/NIOFileSystem/Docs.docc/NIOFileSystem.md

This file was deleted.

Loading

0 comments on commit e469385

Please sign in to comment.