Skip to content

Commit e7e988d

Browse files
committed
Merge #582
582: Add AioCb::from_boxed_slice r=posborne The existing AioCb constructors work for simple programs where everything is stored on the stack. But in more complicated programs the borrow checker can't prove that a buffer will outlive the AioCb that references it. Fix this problem by introducting AioCb::from_boxed_slice, which takes a reference-counted buffer. Fixes #575
2 parents 8c47de4 + d3ad3c7 commit e7e988d

File tree

3 files changed

+105
-34
lines changed

3 files changed

+105
-34
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased]
77

88
### Added
9+
- Added `AioCb::from_boxed_slice`
10+
([#582](https://github.com/nix-rust/nix/pull/582)
911
- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}`
1012
([#551](https://github.com/nix-rust/nix/pull/551))
1113

src/sys/aio.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::io::Write;
88
use std::io::stderr;
99
use std::marker::PhantomData;
1010
use std::mem;
11+
use std::rc::Rc;
1112
use std::ptr::{null, null_mut};
1213
use sys::signal::*;
1314
use sys::time::TimeSpec;
@@ -61,6 +62,17 @@ pub enum AioCancelStat {
6162
AioAllDone = libc::AIO_ALLDONE,
6263
}
6364

65+
/// Private type used by nix to keep buffers from Drop'ing while the kernel has
66+
/// a pointer to them.
67+
#[derive(Clone, Debug)]
68+
enum Keeper<'a> {
69+
none,
70+
/// Keeps a reference to a Boxed slice
71+
boxed(Rc<Box<[u8]>>),
72+
/// Keeps a reference to a slice
73+
phantom(PhantomData<&'a mut [u8]>)
74+
}
75+
6476
/// The basic structure used by all aio functions. Each `aiocb` represents one
6577
/// I/O request.
6678
pub struct AioCb<'a> {
@@ -69,7 +81,8 @@ pub struct AioCb<'a> {
6981
mutable: bool,
7082
/// Could this `AioCb` potentially have any in-kernel state?
7183
in_progress: bool,
72-
phantom: PhantomData<&'a mut [u8]>
84+
/// Used to keep buffers from Drop'ing
85+
keeper: Keeper<'a>
7386
}
7487

7588
impl<'a> AioCb<'a> {
@@ -89,7 +102,7 @@ impl<'a> AioCb<'a> {
89102
a.aio_buf = null_mut();
90103

91104
let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
92-
phantom: PhantomData};
105+
keeper: Keeper::none};
93106
aiocb
94107
}
95108

@@ -106,17 +119,43 @@ impl<'a> AioCb<'a> {
106119
/// which operation to use for this individual aiocb
107120
pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
108121
prio: ::c_int, sigev_notify: SigevNotify,
109-
opcode: LioOpcode) -> AioCb {
122+
opcode: LioOpcode) -> AioCb<'a> {
110123
let mut a = AioCb::common_init(fd, prio, sigev_notify);
111124
a.aio_offset = offs;
112125
a.aio_nbytes = buf.len() as size_t;
113-
// casting an immutable buffer to a mutable pointer looks unsafe, but
114-
// technically its only unsafe to dereference it, not to create it.
115126
a.aio_buf = buf.as_ptr() as *mut c_void;
116127
a.aio_lio_opcode = opcode as ::c_int;
117128

118129
let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false,
119-
phantom: PhantomData};
130+
keeper: Keeper::phantom(PhantomData)};
131+
aiocb
132+
}
133+
134+
/// Constructs a new `AioCb`.
135+
///
136+
/// Unlike `from_mut_slice`, this method returns a structure suitable for
137+
/// placement on the heap.
138+
///
139+
/// * `fd` File descriptor. Required for all aio functions.
140+
/// * `offs` File offset
141+
/// * `buf` A shared memory buffer on the heap
142+
/// * `prio` If POSIX Prioritized IO is supported, then the operation will
143+
/// be prioritized at the process's priority level minus `prio`
144+
/// * `sigev_notify` Determines how you will be notified of event
145+
/// completion.
146+
/// * `opcode` This field is only used for `lio_listio`. It determines
147+
/// which operation to use for this individual aiocb
148+
pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Rc<Box<[u8]>>,
149+
prio: ::c_int, sigev_notify: SigevNotify,
150+
opcode: LioOpcode) -> AioCb<'a> {
151+
let mut a = AioCb::common_init(fd, prio, sigev_notify);
152+
a.aio_offset = offs;
153+
a.aio_nbytes = buf.len() as size_t;
154+
a.aio_buf = buf.as_ptr() as *mut c_void;
155+
a.aio_lio_opcode = opcode as ::c_int;
156+
157+
let aiocb = AioCb{ aiocb: a, mutable: true, in_progress: false,
158+
keeper: Keeper::boxed(buf)};
120159
aiocb
121160
}
122161

@@ -139,12 +178,15 @@ impl<'a> AioCb<'a> {
139178
let mut a = AioCb::common_init(fd, prio, sigev_notify);
140179
a.aio_offset = offs;
141180
a.aio_nbytes = buf.len() as size_t;
181+
// casting an immutable buffer to a mutable pointer looks unsafe,
182+
// but technically its only unsafe to dereference it, not to create
183+
// it.
142184
a.aio_buf = buf.as_ptr() as *mut c_void;
143185
assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
144186
a.aio_lio_opcode = opcode as ::c_int;
145187

146188
let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false,
147-
phantom: PhantomData};
189+
keeper: Keeper::none};
148190
aiocb
149191
}
150192

@@ -284,7 +326,6 @@ impl<'a> Debug for AioCb<'a> {
284326
.field("aio_sigevent", &SigEvent::from(&self.aiocb.aio_sigevent))
285327
.field("mutable", &self.mutable)
286328
.field("in_progress", &self.in_progress)
287-
.field("phantom", &self.phantom)
288329
.finish()
289330
}
290331
}

test/sys/test_aio.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use nix::sys::aio::*;
55
use nix::sys::signal::*;
66
use nix::sys::time::{TimeSpec, TimeValLike};
77
use std::io::{Write, Read, Seek, SeekFrom};
8+
use std::ops::Deref;
89
use std::os::unix::io::AsRawFd;
10+
use std::rc::Rc;
911
use std::sync::Mutex;
1012
use std::sync::atomic::{AtomicBool, Ordering};
1113
use std::{thread, time};
@@ -25,12 +27,12 @@ fn poll_aio(mut aiocb: &mut AioCb) -> Result<()> {
2527
// AioCancelStat value.
2628
#[test]
2729
fn test_cancel() {
28-
let mut wbuf = "CDEF".to_string().into_bytes();
30+
let wbuf: &'static [u8] = b"CDEF";
2931

3032
let f = tempfile().unwrap();
31-
let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
33+
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
3234
0, //offset
33-
&mut wbuf,
35+
&wbuf,
3436
0, //priority
3537
SigevNotify::SigevNone,
3638
LioOpcode::LIO_NOP);
@@ -49,12 +51,12 @@ fn test_cancel() {
4951
// Tests using aio_cancel_all for all outstanding IOs.
5052
#[test]
5153
fn test_aio_cancel_all() {
52-
let mut wbuf = "CDEF".to_string().into_bytes();
54+
let wbuf: &'static [u8] = b"CDEF";
5355

5456
let f = tempfile().unwrap();
55-
let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
57+
let mut aiocb = AioCb::from_slice(f.as_raw_fd(),
5658
0, //offset
57-
&mut wbuf,
59+
&wbuf,
5860
0, //priority
5961
SigevNotify::SigevNone,
6062
LioOpcode::LIO_NOP);
@@ -90,7 +92,7 @@ fn test_aio_suspend() {
9092
const INITIAL: &'static [u8] = b"abcdef123456";
9193
const WBUF: &'static [u8] = b"CDEF";
9294
let timeout = TimeSpec::seconds(10);
93-
let mut rbuf = vec![0; 4];
95+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
9496
let mut f = tempfile().unwrap();
9597
f.write(INITIAL).unwrap();
9698

@@ -101,9 +103,9 @@ fn test_aio_suspend() {
101103
SigevNotify::SigevNone,
102104
LioOpcode::LIO_WRITE);
103105

104-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
106+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
105107
8, //offset
106-
&mut rbuf,
108+
rbuf.clone(),
107109
0, //priority
108110
SigevNotify::SigevNone,
109111
LioOpcode::LIO_READ);
@@ -128,6 +130,31 @@ fn test_aio_suspend() {
128130
// for completion
129131
#[test]
130132
fn test_read() {
133+
const INITIAL: &'static [u8] = b"abcdef123456";
134+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
135+
const EXPECT: &'static [u8] = b"cdef";
136+
let mut f = tempfile().unwrap();
137+
f.write(INITIAL).unwrap();
138+
{
139+
let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
140+
2, //offset
141+
rbuf.clone(),
142+
0, //priority
143+
SigevNotify::SigevNone,
144+
LioOpcode::LIO_NOP);
145+
aiocb.read().unwrap();
146+
147+
let err = poll_aio(&mut aiocb);
148+
assert!(err == Ok(()));
149+
assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len());
150+
}
151+
152+
assert!(EXPECT == rbuf.deref().deref());
153+
}
154+
155+
// Tests from_mut_slice
156+
#[test]
157+
fn test_read_into_mut_slice() {
131158
const INITIAL: &'static [u8] = b"abcdef123456";
132159
let mut rbuf = vec![0; 4];
133160
const EXPECT: &'static [u8] = b"cdef";
@@ -154,7 +181,7 @@ fn test_read() {
154181
#[test]
155182
#[should_panic(expected = "Can't read into an immutable buffer")]
156183
fn test_read_immutable_buffer() {
157-
let rbuf = vec![0; 4];
184+
let rbuf: &'static [u8] = b"CDEF";
158185
let f = tempfile().unwrap();
159186
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
160187
2, //offset
@@ -165,28 +192,29 @@ fn test_read_immutable_buffer() {
165192
aiocb.read().unwrap();
166193
}
167194

195+
168196
// Test a simple aio operation with no completion notification. We must poll
169197
// for completion. Unlike test_aio_read, this test uses AioCb::from_slice
170198
#[test]
171199
fn test_write() {
172200
const INITIAL: &'static [u8] = b"abcdef123456";
173-
const WBUF: &'static [u8] = b"CDEF"; //"CDEF".to_string().into_bytes();
201+
let wbuf = "CDEF".to_string().into_bytes();
174202
let mut rbuf = Vec::new();
175203
const EXPECT: &'static [u8] = b"abCDEF123456";
176204

177205
let mut f = tempfile().unwrap();
178206
f.write(INITIAL).unwrap();
179207
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
180208
2, //offset
181-
&WBUF,
209+
&wbuf,
182210
0, //priority
183211
SigevNotify::SigevNone,
184212
LioOpcode::LIO_NOP);
185213
aiocb.write().unwrap();
186214

187215
let err = poll_aio(&mut aiocb);
188216
assert!(err == Ok(()));
189-
assert!(aiocb.aio_return().unwrap() as usize == WBUF.len());
217+
assert!(aiocb.aio_return().unwrap() as usize == wbuf.len());
190218

191219
f.seek(SeekFrom::Start(0)).unwrap();
192220
let len = f.read_to_end(&mut rbuf).unwrap();
@@ -249,7 +277,7 @@ fn test_write_sigev_signal() {
249277
fn test_lio_listio_wait() {
250278
const INITIAL: &'static [u8] = b"abcdef123456";
251279
const WBUF: &'static [u8] = b"CDEF";
252-
let mut rbuf = vec![0; 4];
280+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
253281
let mut rbuf2 = Vec::new();
254282
const EXPECT: &'static [u8] = b"abCDEF123456";
255283
let mut f = tempfile().unwrap();
@@ -264,9 +292,9 @@ fn test_lio_listio_wait() {
264292
SigevNotify::SigevNone,
265293
LioOpcode::LIO_WRITE);
266294

267-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
295+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
268296
8, //offset
269-
&mut rbuf,
297+
rbuf.clone(),
270298
0, //priority
271299
SigevNotify::SigevNone,
272300
LioOpcode::LIO_READ);
@@ -276,7 +304,7 @@ fn test_lio_listio_wait() {
276304
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
277305
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
278306
}
279-
assert!(rbuf == b"3456");
307+
assert!(rbuf.deref().deref() == b"3456");
280308

281309
f.seek(SeekFrom::Start(0)).unwrap();
282310
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -291,7 +319,7 @@ fn test_lio_listio_wait() {
291319
fn test_lio_listio_nowait() {
292320
const INITIAL: &'static [u8] = b"abcdef123456";
293321
const WBUF: &'static [u8] = b"CDEF";
294-
let mut rbuf = vec![0; 4];
322+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
295323
let mut rbuf2 = Vec::new();
296324
const EXPECT: &'static [u8] = b"abCDEF123456";
297325
let mut f = tempfile().unwrap();
@@ -306,9 +334,9 @@ fn test_lio_listio_nowait() {
306334
SigevNotify::SigevNone,
307335
LioOpcode::LIO_WRITE);
308336

309-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
337+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
310338
8, //offset
311-
&mut rbuf,
339+
rbuf.clone(),
312340
0, //priority
313341
SigevNotify::SigevNone,
314342
LioOpcode::LIO_READ);
@@ -320,7 +348,7 @@ fn test_lio_listio_nowait() {
320348
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
321349
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
322350
}
323-
assert!(rbuf == b"3456");
351+
assert!(rbuf.deref().deref() == b"3456");
324352

325353
f.seek(SeekFrom::Start(0)).unwrap();
326354
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -336,7 +364,7 @@ fn test_lio_listio_signal() {
336364
let _ = SIGUSR2_MTX.lock().expect("Mutex got poisoned by another test");
337365
const INITIAL: &'static [u8] = b"abcdef123456";
338366
const WBUF: &'static [u8] = b"CDEF";
339-
let mut rbuf = vec![0; 4];
367+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
340368
let mut rbuf2 = Vec::new();
341369
const EXPECT: &'static [u8] = b"abCDEF123456";
342370
let mut f = tempfile().unwrap();
@@ -356,9 +384,9 @@ fn test_lio_listio_signal() {
356384
SigevNotify::SigevNone,
357385
LioOpcode::LIO_WRITE);
358386

359-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
387+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
360388
8, //offset
361-
&mut rbuf,
389+
rbuf.clone(),
362390
0, //priority
363391
SigevNotify::SigevNone,
364392
LioOpcode::LIO_READ);
@@ -373,7 +401,7 @@ fn test_lio_listio_signal() {
373401
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
374402
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
375403
}
376-
assert!(rbuf == b"3456");
404+
assert!(rbuf.deref().deref() == b"3456");
377405

378406
f.seek(SeekFrom::Start(0)).unwrap();
379407
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -386,7 +414,7 @@ fn test_lio_listio_signal() {
386414
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
387415
#[should_panic(expected = "Can't read into an immutable buffer")]
388416
fn test_lio_listio_read_immutable() {
389-
let rbuf = vec![0; 4];
417+
let rbuf: &'static [u8] = b"abcd";
390418
let f = tempfile().unwrap();
391419

392420

0 commit comments

Comments
 (0)