-
Notifications
You must be signed in to change notification settings - Fork 130
Add io_uring support to System #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 22 commits
15794f5
6338029
996e940
1f821a8
0762f57
d099546
b584504
b596783
6b4084c
baab9b2
6029936
10c070a
32966f9
822e481
fcb0b69
6f793cf
a9f92a6
1a3e37d
f369347
ef94a37
7ea32ae
55fd6e7
e5fdf9e
fdbceca
7107a57
02481e0
bb03f0f
a396967
d4ca412
5bfed03
74366c3
7f6e673
0c6ef16
a22e5f6
6983196
5ba1377
0064649
901f4c8
283e8d6
d895f2a
f3b8cc4
d338de1
5e24673
49dd797
91155fd
9ad16c0
880ec90
48455a9
72c316b
7fff872
0e652c4
b40b094
27275f8
2f91217
d4ca203
41f9676
014f8b7
261ca25
c029c1b
05eac87
7798f0c
9d325b2
ea77ef8
baa3d62
3760dc2
a98cd3f
344b3b8
c47779f
a49ba8a
f38d72f
3a649e3
7e2f797
f5b8e02
3d6cba2
f9c2a2b
f91cb6b
131326c
321a71c
27ccce5
0d4c6fc
de2d6e2
a6149c2
e580c13
bbe2f1c
227a38f
bc097e9
55e55c1
a6c5a39
528e748
d6687d2
d2b78f8
8757fae
4b70ffe
6dd0eba
329c5f5
a6675d7
d121081
2f6c4e0
779c9ac
7fc787e
f7c05e1
a56bcda
9a04649
b93169b
52d9c99
33b58a8
a66176f
08ea334
08ed962
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,5 +21,6 @@ | |
| #include <pthread.h> | ||
| #include <sched.h> | ||
| #include <unistd.h> | ||
| #include "io_uring.h" | ||
| #endif | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| #include <unistd.h> | ||
| #include <sys/syscall.h> | ||
| #include <sys/uio.h> | ||
|
|
||
| #include <signal.h> | ||
| #include <linux/io_uring.h> | ||
|
|
||
| #ifndef SWIFT_IORING_C_WRAPPER | ||
| #define SWIFT_IORING_C_WRAPPER | ||
|
|
||
| #ifdef __alpha__ | ||
| /* | ||
| * alpha is the only exception, all other architectures | ||
| * have common numbers for new system calls. | ||
| */ | ||
| # ifndef __NR_io_uring_setup | ||
| # define __NR_io_uring_setup 535 | ||
| # endif | ||
| # ifndef __NR_io_uring_enter | ||
| # define __NR_io_uring_enter 536 | ||
| # endif | ||
| # ifndef __NR_io_uring_register | ||
| # define __NR_io_uring_register 537 | ||
| # endif | ||
| #else /* !__alpha__ */ | ||
| # ifndef __NR_io_uring_setup | ||
| # define __NR_io_uring_setup 425 | ||
| # endif | ||
| # ifndef __NR_io_uring_enter | ||
| # define __NR_io_uring_enter 426 | ||
| # endif | ||
| # ifndef __NR_io_uring_register | ||
| # define __NR_io_uring_register 427 | ||
| # endif | ||
| #endif | ||
|
|
||
| int io_uring_register(int fd, unsigned int opcode, void *arg, | ||
| unsigned int nr_args) | ||
| { | ||
| return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args); | ||
| } | ||
|
|
||
| int io_uring_setup(unsigned int entries, struct io_uring_params *p) | ||
| { | ||
| return syscall(__NR_io_uring_setup, entries, p); | ||
| } | ||
|
|
||
| int io_uring_enter(int fd, unsigned int to_submit, unsigned int min_complete, | ||
| unsigned int flags, sigset_t *sig) | ||
| { | ||
| return syscall(__NR_io_uring_enter, fd, to_submit, min_complete, | ||
| flags, sig, _NSIG / 8); | ||
| } | ||
|
|
||
| #endif |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| @_implementationOnly import CSystem | ||
|
|
||
| public struct AsyncFileDescriptor: ~Copyable { | ||
| @usableFromInline var open: Bool = true | ||
| @usableFromInline let fileSlot: IORingFileSlot | ||
| @usableFromInline let ring: ManagedIORing | ||
|
|
||
| public static func open( | ||
| path: FilePath, | ||
| in directory: FileDescriptor = FileDescriptor(rawValue: -100), | ||
| on ring: ManagedIORing, | ||
| mode: FileDescriptor.AccessMode, | ||
| options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(), | ||
| permissions: FilePermissions? = nil | ||
| ) async throws -> AsyncFileDescriptor { | ||
| // todo; real error type | ||
| guard let fileSlot = ring.getFileSlot() else { | ||
| throw IORingError.missingRequiredFeatures | ||
| } | ||
| //TODO: need an async-friendly withCString | ||
| let cstr = path.withCString { | ||
| return $0 // bad | ||
| } | ||
| let res = try await ring.submit(request: IORequest( | ||
| opening: cstr, | ||
| in: directory, | ||
| into: fileSlot, | ||
| mode: mode, | ||
| options: options, | ||
| permissions: permissions | ||
| )) | ||
| if res.result < 0 { | ||
| throw Errno(rawValue: -res.result) | ||
| } | ||
|
|
||
| return AsyncFileDescriptor( | ||
| fileSlot, ring: ring | ||
| ) | ||
| } | ||
|
|
||
| internal init(_ fileSlot: consuming IORingFileSlot, ring: ManagedIORing) { | ||
Catfish-Man marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self.fileSlot = consume fileSlot | ||
| self.ring = ring | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| public consuming func close(isolation actor: isolated (any Actor)? = #isolation) async throws { | ||
Catfish-Man marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let res = try await ring.submit(request: IORequest(closing: fileSlot)) | ||
| if res.result < 0 { | ||
| throw Errno(rawValue: -res.result) | ||
| } | ||
| self.open = false | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| public func read( | ||
| into buffer: inout UnsafeMutableRawBufferPointer, | ||
| atAbsoluteOffset offset: UInt64 = UInt64.max, | ||
| isolation actor: isolated (any Actor)? = #isolation | ||
| ) async throws -> UInt32 { | ||
| let res = try await ring.submit(request: IORequest( | ||
| reading: fileSlot, | ||
| into: buffer, | ||
| at: offset | ||
| )) | ||
| if res.result < 0 { | ||
| throw Errno(rawValue: -res.result) | ||
| } else { | ||
| return UInt32(bitPattern: res.result) | ||
| } | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| public func read( | ||
| into buffer: IORingBuffer, //TODO: should be inout? | ||
| atAbsoluteOffset offset: UInt64 = UInt64.max, | ||
| isolation actor: isolated (any Actor)? = #isolation | ||
| ) async throws -> UInt32 { | ||
| let res = try await ring.submit(request: IORequest( | ||
| reading: fileSlot, | ||
| into: buffer, | ||
| at: offset | ||
| )) | ||
| if res.result < 0 { | ||
| throw Errno(rawValue: -res.result) | ||
| } else { | ||
| return UInt32(bitPattern: res.result) | ||
| } | ||
| } | ||
|
|
||
| //TODO: temporary workaround until AsyncSequence supports ~Copyable | ||
| public consuming func toBytes() -> AsyncFileDescriptorSequence { | ||
| AsyncFileDescriptorSequence(self) | ||
| } | ||
|
|
||
| //TODO: can we do the linear types thing and error if they don't consume it manually? | ||
|
||
| // deinit { | ||
| // if self.open { | ||
| // close() | ||
| // // TODO: close or error? TBD | ||
| // } | ||
| // } | ||
| } | ||
|
|
||
| public class AsyncFileDescriptorSequence: AsyncSequence { | ||
|
||
| var descriptor: AsyncFileDescriptor? | ||
|
|
||
| public func makeAsyncIterator() -> FileIterator { | ||
| return .init(descriptor.take()!) | ||
| } | ||
|
|
||
| internal init(_ descriptor: consuming AsyncFileDescriptor) { | ||
| self.descriptor = consume descriptor | ||
| } | ||
|
|
||
| public typealias AsyncIterator = FileIterator | ||
| public typealias Element = UInt8 | ||
| } | ||
|
|
||
| //TODO: only a class due to ~Copyable limitations | ||
|
||
| public class FileIterator: AsyncIteratorProtocol { | ||
| @usableFromInline let file: AsyncFileDescriptor | ||
| @usableFromInline var buffer: IORingBuffer | ||
| @usableFromInline var done: Bool | ||
|
|
||
| @usableFromInline internal var currentByte: UnsafeRawPointer? | ||
| @usableFromInline internal var lastByte: UnsafeRawPointer? | ||
|
|
||
| init(_ file: consuming AsyncFileDescriptor) { | ||
| self.buffer = file.ring.getBuffer()! | ||
| self.file = file | ||
| self.done = false | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| public func nextBuffer() async throws { | ||
| let bytesRead = Int(try await file.read(into: buffer)) | ||
| if _fastPath(bytesRead != 0) { | ||
| let unsafeBuffer = buffer.unsafeBuffer | ||
| let bufPointer = unsafeBuffer.baseAddress.unsafelyUnwrapped | ||
| self.currentByte = UnsafeRawPointer(bufPointer) | ||
| self.lastByte = UnsafeRawPointer(bufPointer.advanced(by: bytesRead)) | ||
| } else { | ||
| done = true | ||
| } | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| public func next() async throws -> UInt8? { | ||
|
||
| if _fastPath(currentByte != lastByte) { | ||
| // SAFETY: both pointers should be non-nil if they're not equal | ||
| let byte = currentByte.unsafelyUnwrapped.load(as: UInt8.self) | ||
| currentByte = currentByte.unsafelyUnwrapped + 1 | ||
| return byte | ||
| } else if done { | ||
| return nil | ||
| } | ||
| try await nextBuffer() | ||
| return try await next() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| @_implementationOnly import CSystem | ||
|
|
||
| //TODO: should be ~Copyable, but requires UnsafeContinuation add ~Copyable support | ||
|
||
| public struct IOCompletion { | ||
| let rawValue: io_uring_cqe | ||
| } | ||
|
|
||
| extension IOCompletion { | ||
| public struct Flags: OptionSet, Hashable, Codable { | ||
| public let rawValue: UInt32 | ||
|
|
||
| public init(rawValue: UInt32) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| public static let allocatedBuffer = Flags(rawValue: 1 << 0) | ||
Catfish-Man marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public static let moreCompletions = Flags(rawValue: 1 << 1) | ||
| public static let socketNotEmpty = Flags(rawValue: 1 << 2) | ||
| public static let isNotificationEvent = Flags(rawValue: 1 << 3) | ||
| } | ||
| } | ||
|
|
||
| extension IOCompletion { | ||
| public var userData: UInt64 { //TODO: naming? | ||
| get { | ||
| return rawValue.user_data | ||
| } | ||
| } | ||
|
|
||
| public var result: Int32 { | ||
| get { | ||
| return rawValue.res | ||
| } | ||
| } | ||
|
|
||
| public var flags: IOCompletion.Flags { | ||
| get { | ||
| return Flags(rawValue: rawValue.flags & 0x0000FFFF) | ||
| } | ||
| } | ||
|
|
||
| public var bufferIndex: UInt16? { | ||
| get { | ||
| if self.flags.contains(.allocatedBuffer) { | ||
| return UInt16(rawValue.flags >> 16) | ||
| } else { | ||
| return nil | ||
| } | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.