@@ -2,6 +2,8 @@ use {Error, Errno, Result};
2
2
use std:: os:: unix:: io:: RawFd ;
3
3
use libc:: { c_void, off_t, size_t} ;
4
4
use libc;
5
+ use std:: io:: Write ;
6
+ use std:: io:: stderr;
5
7
use std:: marker:: PhantomData ;
6
8
use std:: mem;
7
9
use std:: ptr:: { null, null_mut} ;
@@ -59,11 +61,12 @@ pub enum AioCancelStat {
59
61
60
62
/// The basic structure used by all aio functions. Each `aiocb` represents one
61
63
/// I/O request.
62
- #[ repr( C ) ]
63
64
pub struct AioCb < ' a > {
64
65
aiocb : libc:: aiocb ,
65
66
/// Tracks whether the buffer pointed to by aiocb.aio_buf is mutable
66
67
mutable : bool ,
68
+ /// Could this `AioCb` potentially have any in-kernel state?
69
+ in_progress : bool ,
67
70
phantom : PhantomData < & ' a mut [ u8 ] >
68
71
}
69
72
@@ -83,7 +86,8 @@ impl<'a> AioCb<'a> {
83
86
a. aio_nbytes = 0 ;
84
87
a. aio_buf = null_mut ( ) ;
85
88
86
- let aiocb = AioCb { aiocb : a, mutable : false , phantom : PhantomData } ;
89
+ let aiocb = AioCb { aiocb : a, mutable : false , in_progress : false ,
90
+ phantom : PhantomData } ;
87
91
aiocb
88
92
}
89
93
@@ -109,7 +113,8 @@ impl<'a> AioCb<'a> {
109
113
a. aio_buf = buf. as_ptr ( ) as * mut c_void ;
110
114
a. aio_lio_opcode = opcode as :: c_int ;
111
115
112
- let aiocb = AioCb { aiocb : a, mutable : true , phantom : PhantomData } ;
116
+ let aiocb = AioCb { aiocb : a, mutable : true , in_progress : false ,
117
+ phantom : PhantomData } ;
113
118
aiocb
114
119
}
115
120
@@ -136,7 +141,8 @@ impl<'a> AioCb<'a> {
136
141
assert ! ( opcode != LioOpcode :: LIO_READ , "Can't read into an immutable buffer" ) ;
137
142
a. aio_lio_opcode = opcode as :: c_int ;
138
143
139
- let aiocb = AioCb { aiocb : a, mutable : false , phantom : PhantomData } ;
144
+ let aiocb = AioCb { aiocb : a, mutable : false , in_progress : false ,
145
+ phantom : PhantomData } ;
140
146
aiocb
141
147
}
142
148
@@ -184,13 +190,15 @@ impl<'a> AioCb<'a> {
184
190
/// An asynchronous version of `fsync`.
185
191
pub fn fsync ( & mut self , mode : AioFsyncMode ) -> Result < ( ) > {
186
192
let p: * mut libc:: aiocb = & mut self . aiocb ;
193
+ self . in_progress = true ;
187
194
Errno :: result ( unsafe { libc:: aio_fsync ( mode as :: c_int , p) } ) . map ( drop)
188
195
}
189
196
190
197
/// Asynchronously reads from a file descriptor into a buffer
191
198
pub fn read ( & mut self ) -> Result < ( ) > {
192
199
assert ! ( self . mutable, "Can't read into an immutable buffer" ) ;
193
200
let p: * mut libc:: aiocb = & mut self . aiocb ;
201
+ self . in_progress = true ;
194
202
Errno :: result ( unsafe { libc:: aio_read ( p) } ) . map ( drop)
195
203
}
196
204
@@ -200,12 +208,14 @@ impl<'a> AioCb<'a> {
200
208
// Note: this should be just `return`, but that's a reserved word
201
209
pub fn aio_return ( & mut self ) -> Result < isize > {
202
210
let p: * mut libc:: aiocb = & mut self . aiocb ;
211
+ self . in_progress = false ;
203
212
Errno :: result ( unsafe { libc:: aio_return ( p) } )
204
213
}
205
214
206
215
/// Asynchronously writes from a buffer to a file descriptor
207
216
pub fn write ( & mut self ) -> Result < ( ) > {
208
217
let p: * mut libc:: aiocb = & mut self . aiocb ;
218
+ self . in_progress = true ;
209
219
Errno :: result ( unsafe { libc:: aio_write ( p) } ) . map ( drop)
210
220
}
211
221
@@ -259,3 +269,27 @@ pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
259
269
libc:: lio_listio ( mode as i32 , p, list. len ( ) as i32 , sigevp)
260
270
} ) . map ( drop)
261
271
}
272
+
273
+ impl < ' a > Drop for AioCb < ' a > {
274
+ /// If the `AioCb` has no remaining state in the kernel, just drop it.
275
+ /// Otherwise, collect its error and return values, so as not to leak
276
+ /// resources.
277
+ fn drop ( & mut self ) {
278
+ if self . in_progress {
279
+ // Well-written programs should never get here. They should always
280
+ // wait for an AioCb to complete before dropping it
281
+ let _ = write ! ( stderr( ) , "WARNING: dropped an in-progress AioCb" ) ;
282
+ loop {
283
+ let ret = aio_suspend ( & [ & self ] , None ) ;
284
+ match ret {
285
+ Ok ( ( ) ) => break ,
286
+ Err ( Error :: Sys ( Errno :: EINVAL ) ) => panic ! (
287
+ "Inconsistent AioCb.in_progress value" ) ,
288
+ Err ( Error :: Sys ( Errno :: EINTR ) ) => ( ) , // Retry interrupted syscall
289
+ _ => panic ! ( "Unexpected aio_suspend return value {:?}" , ret)
290
+ } ;
291
+ }
292
+ let _ = self . aio_return ( ) ;
293
+ }
294
+ }
295
+ }
0 commit comments