Skip to content

Commit 3aef80e

Browse files
committed
Implement Drop for AioCb
If an AioCb has any in-kernel state, AioCb.drop will print a warning and wait for it to complete.
1 parent 14608a9 commit 3aef80e

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/sys/aio.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use {Error, Errno, Result};
22
use std::os::unix::io::RawFd;
33
use libc::{c_void, off_t, size_t};
44
use libc;
5+
use std::io::Write;
6+
use std::io::stderr;
57
use std::marker::PhantomData;
68
use std::mem;
79
use std::ptr::{null, null_mut};
@@ -59,11 +61,12 @@ pub enum AioCancelStat {
5961

6062
/// The basic structure used by all aio functions. Each `aiocb` represents one
6163
/// I/O request.
62-
#[repr(C)]
6364
pub struct AioCb<'a> {
6465
aiocb: libc::aiocb,
6566
/// Tracks whether the buffer pointed to by aiocb.aio_buf is mutable
6667
mutable: bool,
68+
/// Could this `AioCb` potentially have any in-kernel state?
69+
in_progress: bool,
6770
phantom: PhantomData<&'a mut [u8]>
6871
}
6972

@@ -83,7 +86,8 @@ impl<'a> AioCb<'a> {
8386
a.aio_nbytes = 0;
8487
a.aio_buf = null_mut();
8588

86-
let aiocb = AioCb { aiocb: a, mutable: false, phantom: PhantomData};
89+
let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
90+
phantom: PhantomData};
8791
aiocb
8892
}
8993

@@ -109,7 +113,8 @@ impl<'a> AioCb<'a> {
109113
a.aio_buf = buf.as_ptr() as *mut c_void;
110114
a.aio_lio_opcode = opcode as ::c_int;
111115

112-
let aiocb = AioCb { aiocb: a, mutable: true, phantom: PhantomData};
116+
let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
117+
phantom: PhantomData};
113118
aiocb
114119
}
115120

@@ -136,7 +141,8 @@ impl<'a> AioCb<'a> {
136141
assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
137142
a.aio_lio_opcode = opcode as ::c_int;
138143

139-
let aiocb = AioCb { aiocb: a, mutable: false, phantom: PhantomData};
144+
let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
145+
phantom: PhantomData};
140146
aiocb
141147
}
142148

@@ -184,13 +190,15 @@ impl<'a> AioCb<'a> {
184190
/// An asynchronous version of `fsync`.
185191
pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> {
186192
let p: *mut libc::aiocb = &mut self.aiocb;
193+
self.in_progress = true;
187194
Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop)
188195
}
189196

190197
/// Asynchronously reads from a file descriptor into a buffer
191198
pub fn read(&mut self) -> Result<()> {
192199
assert!(self.mutable, "Can't read into an immutable buffer");
193200
let p: *mut libc::aiocb = &mut self.aiocb;
201+
self.in_progress = true;
194202
Errno::result(unsafe { libc::aio_read(p) }).map(drop)
195203
}
196204

@@ -200,12 +208,14 @@ impl<'a> AioCb<'a> {
200208
// Note: this should be just `return`, but that's a reserved word
201209
pub fn aio_return(&mut self) -> Result<isize> {
202210
let p: *mut libc::aiocb = &mut self.aiocb;
211+
self.in_progress = false;
203212
Errno::result(unsafe { libc::aio_return(p) })
204213
}
205214

206215
/// Asynchronously writes from a buffer to a file descriptor
207216
pub fn write(&mut self) -> Result<()> {
208217
let p: *mut libc::aiocb = &mut self.aiocb;
218+
self.in_progress = true;
209219
Errno::result(unsafe { libc::aio_write(p) }).map(drop)
210220
}
211221

@@ -259,3 +269,27 @@ pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
259269
libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
260270
}).map(drop)
261271
}
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+
}

test/sys/test_aio.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,30 @@ fn test_lio_listio_read_immutable() {
393393
LioOpcode::LIO_READ);
394394
let _ = lio_listio(LioMode::LIO_NOWAIT, &[&mut rcb], SigevNotify::SigevNone);
395395
}
396+
397+
// Test dropping an AioCb that hasn't yet finished. Behind the scenes, the
398+
// library should wait for the AioCb's completion.
399+
#[test]
400+
fn test_drop() {
401+
const INITIAL: &'static [u8] = b"abcdef123456";
402+
const WBUF: &'static [u8] = b"CDEF"; //"CDEF".to_string().into_bytes();
403+
let mut rbuf = Vec::new();
404+
const EXPECT: &'static [u8] = b"abCDEF123456";
405+
406+
let mut f = tempfile().unwrap();
407+
f.write(INITIAL).unwrap();
408+
{
409+
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
410+
2, //offset
411+
&WBUF,
412+
0, //priority
413+
SigevNotify::SigevNone,
414+
LioOpcode::LIO_NOP);
415+
aiocb.write().unwrap();
416+
}
417+
418+
f.seek(SeekFrom::Start(0)).unwrap();
419+
let len = f.read_to_end(&mut rbuf).unwrap();
420+
assert!(len == EXPECT.len());
421+
assert!(rbuf == EXPECT);
422+
}

0 commit comments

Comments
 (0)