Skip to content

Commit f175ea7

Browse files
committed
Add AioCb::from_boxed_slice
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
1 parent 635c871 commit f175ea7

File tree

3 files changed

+105
-34
lines changed

3 files changed

+105
-34
lines changed

CHANGELOG.md

+2
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

+49-8
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

+54-26
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::{thread, time};
1012
use tempfile::tempfile;
1113

@@ -23,12 +25,12 @@ fn poll_aio(mut aiocb: &mut AioCb) -> Result<()> {
2325
// AioCancelStat value.
2426
#[test]
2527
fn test_cancel() {
26-
let mut wbuf = "CDEF".to_string().into_bytes();
28+
let wbuf: &'static [u8] = b"CDEF";
2729

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

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

@@ -99,9 +101,9 @@ fn test_aio_suspend() {
99101
SigevNotify::SigevNone,
100102
LioOpcode::LIO_WRITE);
101103

102-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
104+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
103105
8, //offset
104-
&mut rbuf,
106+
rbuf.clone(),
105107
0, //priority
106108
SigevNotify::SigevNone,
107109
LioOpcode::LIO_READ);
@@ -126,6 +128,31 @@ fn test_aio_suspend() {
126128
// for completion
127129
#[test]
128130
fn test_read() {
131+
const INITIAL: &'static [u8] = b"abcdef123456";
132+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
133+
const EXPECT: &'static [u8] = b"cdef";
134+
let mut f = tempfile().unwrap();
135+
f.write(INITIAL).unwrap();
136+
{
137+
let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
138+
2, //offset
139+
rbuf.clone(),
140+
0, //priority
141+
SigevNotify::SigevNone,
142+
LioOpcode::LIO_NOP);
143+
aiocb.read().unwrap();
144+
145+
let err = poll_aio(&mut aiocb);
146+
assert!(err == Ok(()));
147+
assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len());
148+
}
149+
150+
assert!(EXPECT == rbuf.deref().deref());
151+
}
152+
153+
// Tests from_mut_slice
154+
#[test]
155+
fn test_read_into_mut_slice() {
129156
const INITIAL: &'static [u8] = b"abcdef123456";
130157
let mut rbuf = vec![0; 4];
131158
const EXPECT: &'static [u8] = b"cdef";
@@ -152,7 +179,7 @@ fn test_read() {
152179
#[test]
153180
#[should_panic(expected = "Can't read into an immutable buffer")]
154181
fn test_read_immutable_buffer() {
155-
let rbuf = vec![0; 4];
182+
let rbuf: &'static [u8] = b"CDEF";
156183
let f = tempfile().unwrap();
157184
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
158185
2, //offset
@@ -163,28 +190,29 @@ fn test_read_immutable_buffer() {
163190
aiocb.read().unwrap();
164191
}
165192

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

175203
let mut f = tempfile().unwrap();
176204
f.write(INITIAL).unwrap();
177205
let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
178206
2, //offset
179-
&WBUF,
207+
&wbuf,
180208
0, //priority
181209
SigevNotify::SigevNone,
182210
LioOpcode::LIO_NOP);
183211
aiocb.write().unwrap();
184212

185213
let err = poll_aio(&mut aiocb);
186214
assert!(err == Ok(()));
187-
assert!(aiocb.aio_return().unwrap() as usize == WBUF.len());
215+
assert!(aiocb.aio_return().unwrap() as usize == wbuf.len());
188216

189217
f.seek(SeekFrom::Start(0)).unwrap();
190218
let len = f.read_to_end(&mut rbuf).unwrap();
@@ -245,7 +273,7 @@ fn test_write_sigev_signal() {
245273
fn test_lio_listio_wait() {
246274
const INITIAL: &'static [u8] = b"abcdef123456";
247275
const WBUF: &'static [u8] = b"CDEF";
248-
let mut rbuf = vec![0; 4];
276+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
249277
let mut rbuf2 = Vec::new();
250278
const EXPECT: &'static [u8] = b"abCDEF123456";
251279
let mut f = tempfile().unwrap();
@@ -260,9 +288,9 @@ fn test_lio_listio_wait() {
260288
SigevNotify::SigevNone,
261289
LioOpcode::LIO_WRITE);
262290

263-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
291+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
264292
8, //offset
265-
&mut rbuf,
293+
rbuf.clone(),
266294
0, //priority
267295
SigevNotify::SigevNone,
268296
LioOpcode::LIO_READ);
@@ -272,7 +300,7 @@ fn test_lio_listio_wait() {
272300
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
273301
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
274302
}
275-
assert!(rbuf == b"3456");
303+
assert!(rbuf.deref().deref() == b"3456");
276304

277305
f.seek(SeekFrom::Start(0)).unwrap();
278306
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -287,7 +315,7 @@ fn test_lio_listio_wait() {
287315
fn test_lio_listio_nowait() {
288316
const INITIAL: &'static [u8] = b"abcdef123456";
289317
const WBUF: &'static [u8] = b"CDEF";
290-
let mut rbuf = vec![0; 4];
318+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
291319
let mut rbuf2 = Vec::new();
292320
const EXPECT: &'static [u8] = b"abCDEF123456";
293321
let mut f = tempfile().unwrap();
@@ -302,9 +330,9 @@ fn test_lio_listio_nowait() {
302330
SigevNotify::SigevNone,
303331
LioOpcode::LIO_WRITE);
304332

305-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
333+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
306334
8, //offset
307-
&mut rbuf,
335+
rbuf.clone(),
308336
0, //priority
309337
SigevNotify::SigevNone,
310338
LioOpcode::LIO_READ);
@@ -316,7 +344,7 @@ fn test_lio_listio_nowait() {
316344
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
317345
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
318346
}
319-
assert!(rbuf == b"3456");
347+
assert!(rbuf.deref().deref() == b"3456");
320348

321349
f.seek(SeekFrom::Start(0)).unwrap();
322350
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -331,7 +359,7 @@ fn test_lio_listio_nowait() {
331359
fn test_lio_listio_signal() {
332360
const INITIAL: &'static [u8] = b"abcdef123456";
333361
const WBUF: &'static [u8] = b"CDEF";
334-
let mut rbuf = vec![0; 4];
362+
let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
335363
let mut rbuf2 = Vec::new();
336364
const EXPECT: &'static [u8] = b"abCDEF123456";
337365
let mut f = tempfile().unwrap();
@@ -351,9 +379,9 @@ fn test_lio_listio_signal() {
351379
SigevNotify::SigevNone,
352380
LioOpcode::LIO_WRITE);
353381

354-
let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
382+
let mut rcb = AioCb::from_boxed_slice( f.as_raw_fd(),
355383
8, //offset
356-
&mut rbuf,
384+
rbuf.clone(),
357385
0, //priority
358386
SigevNotify::SigevNone,
359387
LioOpcode::LIO_READ);
@@ -368,7 +396,7 @@ fn test_lio_listio_signal() {
368396
assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
369397
assert!(rcb.aio_return().unwrap() as usize == WBUF.len());
370398
}
371-
assert!(rbuf == b"3456");
399+
assert!(rbuf.deref().deref() == b"3456");
372400

373401
f.seek(SeekFrom::Start(0)).unwrap();
374402
let len = f.read_to_end(&mut rbuf2).unwrap();
@@ -381,7 +409,7 @@ fn test_lio_listio_signal() {
381409
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
382410
#[should_panic(expected = "Can't read into an immutable buffer")]
383411
fn test_lio_listio_read_immutable() {
384-
let rbuf = vec![0; 4];
412+
let rbuf: &'static [u8] = b"abcd";
385413
let f = tempfile().unwrap();
386414

387415

0 commit comments

Comments
 (0)