IORequest.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. #if compiler(>=6.2) && $Lifetimes
  2. #if os(Linux)
  3. import CSystem
  4. @usableFromInline
  5. internal enum IORequestCore {
  6. case nop // nothing here
  7. case openat(
  8. atDirectory: FileDescriptor,
  9. path: FilePath,
  10. FileDescriptor.AccessMode,
  11. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  12. permissions: FilePermissions? = nil,
  13. context: UInt64 = 0
  14. )
  15. case openatSlot(
  16. atDirectory: FileDescriptor,
  17. path: FilePath,
  18. FileDescriptor.AccessMode,
  19. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  20. permissions: FilePermissions? = nil,
  21. intoSlot: IORing.RegisteredFile,
  22. context: UInt64 = 0
  23. )
  24. case read(
  25. file: FileDescriptor,
  26. buffer: IORing.RegisteredBuffer,
  27. offset: UInt64 = 0,
  28. context: UInt64 = 0
  29. )
  30. case readUnregistered(
  31. file: FileDescriptor,
  32. buffer: UnsafeMutableRawBufferPointer,
  33. offset: UInt64 = 0,
  34. context: UInt64 = 0
  35. )
  36. case readSlot(
  37. file: IORing.RegisteredFile,
  38. buffer: IORing.RegisteredBuffer,
  39. offset: UInt64 = 0,
  40. context: UInt64 = 0
  41. )
  42. case readUnregisteredSlot(
  43. file: IORing.RegisteredFile,
  44. buffer: UnsafeMutableRawBufferPointer,
  45. offset: UInt64 = 0,
  46. context: UInt64 = 0
  47. )
  48. case write(
  49. file: FileDescriptor,
  50. buffer: IORing.RegisteredBuffer,
  51. offset: UInt64 = 0,
  52. context: UInt64 = 0
  53. )
  54. case writeUnregistered(
  55. file: FileDescriptor,
  56. buffer: UnsafeMutableRawBufferPointer,
  57. offset: UInt64 = 0,
  58. context: UInt64 = 0
  59. )
  60. case writeSlot(
  61. file: IORing.RegisteredFile,
  62. buffer: IORing.RegisteredBuffer,
  63. offset: UInt64 = 0,
  64. context: UInt64 = 0
  65. )
  66. case writeUnregisteredSlot(
  67. file: IORing.RegisteredFile,
  68. buffer: UnsafeMutableRawBufferPointer,
  69. offset: UInt64 = 0,
  70. context: UInt64 = 0
  71. )
  72. case close(
  73. FileDescriptor,
  74. context: UInt64 = 0
  75. )
  76. case closeSlot(
  77. IORing.RegisteredFile,
  78. context: UInt64 = 0
  79. )
  80. case unlinkAt(
  81. atDirectory: FileDescriptor,
  82. path: FilePath,
  83. context: UInt64 = 0
  84. )
  85. case cancel(
  86. flags:UInt32
  87. )
  88. case cancelContext(
  89. flags: UInt32,
  90. targetContext: UInt64
  91. )
  92. case cancelFD(
  93. flags: UInt32,
  94. targetFD: FileDescriptor
  95. )
  96. case cancelFDSlot(
  97. flags: UInt32,
  98. target: IORing.RegisteredFile
  99. )
  100. }
  101. @inline(__always) @inlinable
  102. internal func makeRawRequest_readWrite_registered(
  103. file: FileDescriptor,
  104. buffer: IORing.RegisteredBuffer,
  105. offset: UInt64,
  106. context: UInt64 = 0,
  107. request: consuming RawIORequest
  108. ) -> RawIORequest {
  109. request.fileDescriptor = file
  110. request.buffer = buffer.unsafeBuffer
  111. request.rawValue.buf_index = UInt16(exactly: buffer.index)!
  112. request.offset = offset
  113. request.rawValue.user_data = context
  114. return request
  115. }
  116. @inline(__always) @inlinable
  117. internal func makeRawRequest_readWrite_registered_slot(
  118. file: IORing.RegisteredFile,
  119. buffer: IORing.RegisteredBuffer,
  120. offset: UInt64,
  121. context: UInt64 = 0,
  122. request: consuming RawIORequest
  123. ) -> RawIORequest {
  124. request.rawValue.fd = Int32(exactly: file.index)!
  125. request.flags = .fixedFile
  126. request.buffer = buffer.unsafeBuffer
  127. request.rawValue.buf_index = UInt16(exactly: buffer.index)!
  128. request.offset = offset
  129. request.rawValue.user_data = context
  130. return request
  131. }
  132. @inline(__always) @inlinable
  133. internal func makeRawRequest_readWrite_unregistered(
  134. file: FileDescriptor,
  135. buffer: UnsafeMutableRawBufferPointer,
  136. offset: UInt64,
  137. context: UInt64 = 0,
  138. request: consuming RawIORequest
  139. ) -> RawIORequest {
  140. request.fileDescriptor = file
  141. request.buffer = buffer
  142. request.offset = offset
  143. request.rawValue.user_data = context
  144. return request
  145. }
  146. @inline(__always) @inlinable
  147. internal func makeRawRequest_readWrite_unregistered_slot(
  148. file: IORing.RegisteredFile,
  149. buffer: UnsafeMutableRawBufferPointer,
  150. offset: UInt64,
  151. context: UInt64 = 0,
  152. request: consuming RawIORequest
  153. ) -> RawIORequest {
  154. request.rawValue.fd = Int32(exactly: file.index)!
  155. request.flags = .fixedFile
  156. request.buffer = buffer
  157. request.offset = offset
  158. request.rawValue.user_data = context
  159. return request
  160. }
  161. extension IORing {
  162. public struct Request {
  163. @usableFromInline var core: IORequestCore
  164. @inlinable internal init(core inCore: IORequestCore) {
  165. core = inCore
  166. }
  167. @inlinable internal consuming func extractCore() -> IORequestCore {
  168. return core
  169. }
  170. }
  171. }
  172. extension IORing.Request {
  173. @inlinable public static func nop(context: UInt64 = 0) -> IORing.Request {
  174. .init(core: .nop)
  175. }
  176. @inlinable public static func read(
  177. _ file: IORing.RegisteredFile,
  178. into buffer: IORing.RegisteredBuffer,
  179. at offset: UInt64 = 0,
  180. context: UInt64 = 0
  181. ) -> IORing.Request {
  182. .init(core: .readSlot(file: file, buffer: buffer, offset: offset, context: context))
  183. }
  184. @inlinable public static func read(
  185. _ file: FileDescriptor,
  186. into buffer: IORing.RegisteredBuffer,
  187. at offset: UInt64 = 0,
  188. context: UInt64 = 0
  189. ) -> IORing.Request {
  190. .init(core: .read(file: file, buffer: buffer, offset: offset, context: context))
  191. }
  192. @inlinable public static func read(
  193. _ file: IORing.RegisteredFile,
  194. into buffer: UnsafeMutableRawBufferPointer,
  195. at offset: UInt64 = 0,
  196. context: UInt64 = 0
  197. ) -> IORing.Request {
  198. .init(core: .readUnregisteredSlot(file: file, buffer: buffer, offset: offset, context: context))
  199. }
  200. @inlinable public static func read(
  201. _ file: FileDescriptor,
  202. into buffer: UnsafeMutableRawBufferPointer,
  203. at offset: UInt64 = 0,
  204. context: UInt64 = 0
  205. ) -> IORing.Request {
  206. .init(core: .readUnregistered(file: file, buffer: buffer, offset: offset, context: context))
  207. }
  208. @inlinable public static func write(
  209. _ buffer: IORing.RegisteredBuffer,
  210. into file: IORing.RegisteredFile,
  211. at offset: UInt64 = 0,
  212. context: UInt64 = 0
  213. ) -> IORing.Request {
  214. .init(core: .writeSlot(file: file, buffer: buffer, offset: offset, context: context))
  215. }
  216. @inlinable public static func write(
  217. _ buffer: IORing.RegisteredBuffer,
  218. into file: FileDescriptor,
  219. at offset: UInt64 = 0,
  220. context: UInt64 = 0
  221. ) -> IORing.Request {
  222. .init(core: .write(file: file, buffer: buffer, offset: offset, context: context))
  223. }
  224. @inlinable public static func write(
  225. _ buffer: UnsafeMutableRawBufferPointer,
  226. into file: IORing.RegisteredFile,
  227. at offset: UInt64 = 0,
  228. context: UInt64 = 0
  229. ) -> IORing.Request {
  230. .init(core: .writeUnregisteredSlot(
  231. file: file, buffer: buffer, offset: offset, context: context))
  232. }
  233. @inlinable public static func write(
  234. _ buffer: UnsafeMutableRawBufferPointer,
  235. into file: FileDescriptor,
  236. at offset: UInt64 = 0,
  237. context: UInt64 = 0
  238. ) -> IORing.Request {
  239. .init(
  240. core: .writeUnregistered(file: file, buffer: buffer, offset: offset, context: context)
  241. )
  242. }
  243. @inlinable public static func close(
  244. _ file: FileDescriptor,
  245. context: UInt64 = 0
  246. ) -> IORing.Request {
  247. .init(core: .close(file, context: context))
  248. }
  249. @inlinable public static func close(
  250. _ file: IORing.RegisteredFile,
  251. context: UInt64 = 0
  252. ) -> IORing.Request {
  253. .init(core: .closeSlot(file, context: context))
  254. }
  255. @inlinable public static func open(
  256. _ path: FilePath,
  257. in directory: FileDescriptor,
  258. into slot: IORing.RegisteredFile,
  259. mode: FileDescriptor.AccessMode,
  260. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  261. permissions: FilePermissions? = nil,
  262. context: UInt64 = 0
  263. ) -> IORing.Request {
  264. .init(
  265. core: .openatSlot(
  266. atDirectory: directory, path: path, mode, options: options,
  267. permissions: permissions, intoSlot: slot, context: context))
  268. }
  269. @inlinable public static func open(
  270. _ path: FilePath,
  271. in directory: FileDescriptor,
  272. mode: FileDescriptor.AccessMode,
  273. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  274. permissions: FilePermissions? = nil,
  275. context: UInt64 = 0
  276. ) -> IORing.Request {
  277. .init(
  278. core: .openat(
  279. atDirectory: directory, path: path, mode, options: options,
  280. permissions: permissions, context: context
  281. ))
  282. }
  283. @inlinable public static func unlink(
  284. _ path: FilePath,
  285. in directory: FileDescriptor,
  286. context: UInt64 = 0
  287. ) -> IORing.Request {
  288. .init(core: .unlinkAt(atDirectory: directory, path: path, context: context))
  289. }
  290. // Cancel
  291. /*
  292. * ASYNC_CANCEL flags.
  293. *
  294. * IORING_ASYNC_CANCEL_ALL Cancel all requests that match the given key
  295. * IORING_ASYNC_CANCEL_FD Key off 'fd' for cancellation rather than the
  296. * request 'user_data'
  297. * IORING_ASYNC_CANCEL_ANY Match any request
  298. * IORING_ASYNC_CANCEL_FD_FIXED 'fd' passed in is a fixed descriptor
  299. * IORING_ASYNC_CANCEL_USERDATA Match on user_data, default for no other key
  300. * IORING_ASYNC_CANCEL_OP Match request based on opcode
  301. */
  302. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_ALL: UInt32 { 1 << 0 }
  303. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_FD: UInt32 { 1 << 1 }
  304. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_ANY: UInt32 { 1 << 2 }
  305. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_FD_FIXED: UInt32 { 1 << 3 }
  306. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_USERDATA: UInt32 { 1 << 4 }
  307. @inlinable internal static var SWIFT_IORING_ASYNC_CANCEL_OP: UInt32 { 1 << 5 }
  308. public enum CancellationMatch {
  309. case all
  310. case first
  311. }
  312. @inlinable public static func cancel(
  313. _ matchAll: CancellationMatch,
  314. matchingContext: UInt64
  315. ) -> IORing.Request {
  316. switch matchAll {
  317. case .all:
  318. .init(core: .cancelContext(flags: SWIFT_IORING_ASYNC_CANCEL_ALL | SWIFT_IORING_ASYNC_CANCEL_USERDATA, targetContext: matchingContext))
  319. case .first:
  320. .init(core: .cancelContext(flags: SWIFT_IORING_ASYNC_CANCEL_USERDATA, targetContext: matchingContext))
  321. }
  322. }
  323. @inlinable public static func cancel(
  324. _ matchAll: CancellationMatch,
  325. matching: FileDescriptor
  326. ) -> IORing.Request {
  327. switch matchAll {
  328. case .all:
  329. .init(core: .cancelFD(flags: SWIFT_IORING_ASYNC_CANCEL_ALL | SWIFT_IORING_ASYNC_CANCEL_FD, targetFD: matching))
  330. case .first:
  331. .init(core: .cancelFD(flags: SWIFT_IORING_ASYNC_CANCEL_FD, targetFD: matching))
  332. }
  333. }
  334. @inlinable public static func cancel(
  335. _ matchAll: CancellationMatch,
  336. matching: IORing.RegisteredFile
  337. ) -> IORing.Request {
  338. switch matchAll {
  339. case .all:
  340. .init(core: .cancelFDSlot(flags: SWIFT_IORING_ASYNC_CANCEL_ALL | SWIFT_IORING_ASYNC_CANCEL_FD_FIXED, target: matching))
  341. case .first:
  342. .init(core: .cancelFDSlot(flags: SWIFT_IORING_ASYNC_CANCEL_FD_FIXED, target: matching))
  343. }
  344. }
  345. @inlinable public static func cancel(
  346. _ matchAll: CancellationMatch,
  347. ) -> IORing.Request {
  348. switch matchAll {
  349. case .all:
  350. .init(core: .cancel(flags: SWIFT_IORING_ASYNC_CANCEL_ALL))
  351. case .first:
  352. .init(core: .cancel(flags: SWIFT_IORING_ASYNC_CANCEL_ANY))
  353. }
  354. }
  355. @inline(__always) @inlinable
  356. internal consuming func makeRawRequest(
  357. pathBuffers: PendingPathBuffers
  358. ) -> RawIORequest {
  359. var request = RawIORequest()
  360. switch extractCore() {
  361. case .nop:
  362. request.operation = .nop
  363. case .openatSlot(
  364. let atDirectory, let path, let mode, let options, let permissions, let fileSlot,
  365. let context):
  366. // TODO: use rawValue less
  367. request.operation = .openAt
  368. request.fileDescriptor = atDirectory
  369. request.rawValue.addr = UInt64(
  370. UInt(bitPattern: pathBuffers.pin(path)))
  371. request.rawValue.open_flags = UInt32(bitPattern: options.rawValue | mode.rawValue)
  372. request.rawValue.len = permissions?.rawValue ?? 0
  373. request.rawValue.file_index = UInt32(fileSlot.index + 1)
  374. request.rawValue.user_data = context
  375. case .openat(
  376. let atDirectory, let path, let mode, let options, let permissions, let context):
  377. request.operation = .openAt
  378. request.fileDescriptor = atDirectory
  379. request.rawValue.addr = UInt64(
  380. UInt(bitPattern: pathBuffers.pin(path)))
  381. request.rawValue.open_flags = UInt32(bitPattern: options.rawValue | mode.rawValue)
  382. request.rawValue.len = permissions?.rawValue ?? 0
  383. request.rawValue.user_data = context
  384. case .write(let file, let buffer, let offset, let context):
  385. request.operation = .writeFixed
  386. return makeRawRequest_readWrite_registered(
  387. file: file, buffer: buffer, offset: offset, context: context, request: request)
  388. case .writeSlot(let file, let buffer, let offset, let context):
  389. request.operation = .writeFixed
  390. return makeRawRequest_readWrite_registered_slot(
  391. file: file, buffer: buffer, offset: offset, context: context, request: request)
  392. case .writeUnregistered(let file, let buffer, let offset, let context):
  393. request.operation = .write
  394. return makeRawRequest_readWrite_unregistered(
  395. file: file, buffer: buffer, offset: offset, context: context, request: request)
  396. case .writeUnregisteredSlot(let file, let buffer, let offset, let context):
  397. request.operation = .write
  398. return makeRawRequest_readWrite_unregistered_slot(
  399. file: file, buffer: buffer, offset: offset, context: context, request: request)
  400. case .read(let file, let buffer, let offset, let context):
  401. request.operation = .readFixed
  402. return makeRawRequest_readWrite_registered(
  403. file: file, buffer: buffer, offset: offset, context: context, request: request)
  404. case .readSlot(let file, let buffer, let offset, let context):
  405. request.operation = .readFixed
  406. return makeRawRequest_readWrite_registered_slot(
  407. file: file, buffer: buffer, offset: offset, context: context, request: request)
  408. case .readUnregistered(let file, let buffer, let offset, let context):
  409. request.operation = .read
  410. return makeRawRequest_readWrite_unregistered(
  411. file: file, buffer: buffer, offset: offset, context: context, request: request)
  412. case .readUnregisteredSlot(let file, let buffer, let offset, let context):
  413. request.operation = .read
  414. return makeRawRequest_readWrite_unregistered_slot(
  415. file: file, buffer: buffer, offset: offset, context: context, request: request)
  416. case .close(let file, let context):
  417. request.operation = .close
  418. request.fileDescriptor = file
  419. request.rawValue.user_data = context
  420. case .closeSlot(let file, let context):
  421. request.operation = .close
  422. request.rawValue.file_index = UInt32(file.index + 1)
  423. request.rawValue.user_data = context
  424. case .unlinkAt(let atDirectory, let path, let context):
  425. request.operation = .unlinkAt
  426. request.fileDescriptor = atDirectory
  427. request.rawValue.addr = UInt64(
  428. UInt(bitPattern: pathBuffers.pin(path)))
  429. request.rawValue.user_data = context
  430. case .cancelContext(let flags, let targetContext):
  431. request.operation = .asyncCancel
  432. request.cancel_flags = flags
  433. request.addr = targetContext
  434. case .cancelFD(let flags, let targetFD):
  435. request.operation = .asyncCancel
  436. request.cancel_flags = flags
  437. request.fileDescriptor = targetFD
  438. case .cancelFDSlot(let flags, let target):
  439. request.operation = .asyncCancel
  440. request.cancel_flags = flags
  441. request.rawValue.fd = Int32(target.index)
  442. case .cancel(let flags):
  443. request.operation = .asyncCancel
  444. request.cancel_flags = flags
  445. }
  446. return request
  447. }
  448. }
  449. #endif // os(Linux)
  450. #endif // compiler(>=6.2) && $Lifetimes