1
1
#[ cfg_attr( all( doc, docsrs) , doc( cfg( all( ) ) ) ) ]
2
2
#[ allow( unused_imports) ]
3
3
pub use std:: os:: fd:: { AsRawFd , FromRawFd , IntoRawFd , RawFd } ;
4
- use std:: { collections:: VecDeque , io, os:: fd:: OwnedFd , pin:: Pin , task:: Poll , time:: Duration } ;
4
+ use std:: {
5
+ collections:: VecDeque , io, os:: fd:: OwnedFd , pin:: Pin , ptr:: NonNull , sync:: Arc , task:: Poll ,
6
+ time:: Duration ,
7
+ } ;
5
8
6
9
use compio_log:: { instrument, trace} ;
10
+ use crossbeam_queue:: SegQueue ;
7
11
use io_uring:: {
8
12
cqueue,
9
13
opcode:: { AsyncCancel , Read } ,
@@ -14,15 +18,35 @@ use io_uring::{
14
18
pub ( crate ) use libc:: { sockaddr_storage, socklen_t} ;
15
19
use slab:: Slab ;
16
20
17
- use crate :: { syscall, Entry , ProactorBuilder } ;
21
+ use crate :: { syscall, AsyncifyPool , Entry , ProactorBuilder } ;
18
22
19
23
pub ( crate ) mod op;
20
24
pub ( crate ) use crate :: unix:: RawOp ;
21
25
26
+ /// The created entry of [`OpCode`].
27
+ pub enum OpEntry {
28
+ /// This operation creates an io-uring submission entry.
29
+ Submission ( squeue:: Entry ) ,
30
+ /// This operation is a blocking one.
31
+ Blocking ,
32
+ }
33
+
34
+ impl From < squeue:: Entry > for OpEntry {
35
+ fn from ( value : squeue:: Entry ) -> Self {
36
+ Self :: Submission ( value)
37
+ }
38
+ }
39
+
22
40
/// Abstraction of io-uring operations.
23
41
pub trait OpCode {
24
42
/// Create submission entry.
25
- fn create_entry ( self : Pin < & mut Self > ) -> squeue:: Entry ;
43
+ fn create_entry ( self : Pin < & mut Self > ) -> OpEntry ;
44
+
45
+ /// Call the operation in a blocking way. This method will only be called if
46
+ /// [`create_entry`] returns [`OpEntry::Blocking`].
47
+ fn call_blocking ( self : Pin < & mut Self > ) -> io:: Result < usize > {
48
+ unreachable ! ( "this operation is asynchronous" )
49
+ }
26
50
}
27
51
28
52
/// Low-level driver of io-uring.
@@ -31,6 +55,8 @@ pub(crate) struct Driver {
31
55
squeue : VecDeque < squeue:: Entry > ,
32
56
notifier : Notifier ,
33
57
notifier_registered : bool ,
58
+ pool : AsyncifyPool ,
59
+ pool_completed : Arc < SegQueue < Entry > > ,
34
60
}
35
61
36
62
impl Driver {
@@ -45,6 +71,8 @@ impl Driver {
45
71
squeue : VecDeque :: with_capacity ( builder. capacity as usize ) ,
46
72
notifier : Notifier :: new ( ) ?,
47
73
notifier_registered : false ,
74
+ pool : builder. create_or_get_thread_pool ( ) ,
75
+ pool_completed : Arc :: new ( SegQueue :: new ( ) ) ,
48
76
} )
49
77
}
50
78
@@ -118,6 +146,10 @@ impl Driver {
118
146
}
119
147
120
148
fn poll_entries ( & mut self , entries : & mut impl Extend < Entry > ) {
149
+ while let Some ( entry) = self . pool_completed . pop ( ) {
150
+ entries. extend ( Some ( entry) ) ;
151
+ }
152
+
121
153
let mut cqueue = self . inner . completion ( ) ;
122
154
cqueue. sync ( ) ;
123
155
let completed_entries = cqueue. filter_map ( |entry| match entry. user_data ( ) {
@@ -147,11 +179,39 @@ impl Driver {
147
179
148
180
pub fn push ( & mut self , user_data : usize , op : & mut RawOp ) -> Poll < io:: Result < usize > > {
149
181
instrument ! ( compio_log:: Level :: TRACE , "push" , user_data) ;
150
- let op = op. as_pin ( ) ;
182
+ let op_pin = op. as_pin ( ) ;
151
183
trace ! ( "push RawOp" ) ;
152
- self . squeue
153
- . push_back ( op. create_entry ( ) . user_data ( user_data as _ ) ) ;
154
- Poll :: Pending
184
+ if let OpEntry :: Submission ( entry) = op_pin. create_entry ( ) {
185
+ self . squeue . push_back ( entry. user_data ( user_data as _ ) ) ;
186
+ Poll :: Pending
187
+ } else if self . push_blocking ( user_data, op) ? {
188
+ Poll :: Pending
189
+ } else {
190
+ Poll :: Ready ( Err ( io:: Error :: from_raw_os_error ( libc:: EBUSY ) ) )
191
+ }
192
+ }
193
+
194
+ fn push_blocking ( & mut self , user_data : usize , op : & mut RawOp ) -> io:: Result < bool > {
195
+ // Safety: the RawOp is not released before the operation returns.
196
+ struct SendWrapper < T > ( T ) ;
197
+ unsafe impl < T > Send for SendWrapper < T > { }
198
+
199
+ let op = SendWrapper ( NonNull :: from ( op) ) ;
200
+ let handle = self . handle ( ) ?;
201
+ let completed = self . pool_completed . clone ( ) ;
202
+ let is_ok = self
203
+ . pool
204
+ . dispatch ( move || {
205
+ #[ allow( clippy:: redundant_locals) ]
206
+ let mut op = op;
207
+ let op = unsafe { op. 0 . as_mut ( ) } ;
208
+ let op_pin = op. as_pin ( ) ;
209
+ let res = op_pin. call_blocking ( ) ;
210
+ completed. push ( Entry :: new ( user_data, res) ) ;
211
+ handle. notify ( ) . ok ( ) ;
212
+ } )
213
+ . is_ok ( ) ;
214
+ Ok ( is_ok)
155
215
}
156
216
157
217
pub unsafe fn poll (
0 commit comments