• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use libc::{c_int, c_void};
2 use nix::Result;
3 use nix::errno::*;
4 use nix::sys::aio::*;
5 use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
6 use nix::sys::time::{TimeSpec, TimeValLike};
7 use std::io::{Write, Read, Seek, SeekFrom};
8 use std::ops::Deref;
9 use std::os::unix::io::AsRawFd;
10 use std::pin::Pin;
11 use std::sync::atomic::{AtomicBool, Ordering};
12 use std::{thread, time};
13 use tempfile::tempfile;
14 
15 // Helper that polls an AioCb for completion or error
poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()>16 fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
17     loop {
18         let err = aiocb.error();
19         if err != Err(Errno::EINPROGRESS) { return err; };
20         thread::sleep(time::Duration::from_millis(10));
21     }
22 }
23 
24 // Helper that polls a component of an LioCb for completion or error
25 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
poll_lio(liocb: &mut LioCb, i: usize) -> Result<()>26 fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
27     loop {
28         let err = liocb.error(i);
29         if err != Err(Errno::EINPROGRESS) { return err; };
30         thread::sleep(time::Duration::from_millis(10));
31     }
32 }
33 
34 #[test]
test_accessors()35 fn test_accessors() {
36     let mut rbuf = vec![0; 4];
37     let aiocb = AioCb::from_mut_slice( 1001,
38                            2,   //offset
39                            &mut rbuf,
40                            42,   //priority
41                            SigevNotify::SigevSignal {
42                                signal: Signal::SIGUSR2,
43                                si_value: 99
44                            },
45                            LioOpcode::LIO_NOP);
46     assert_eq!(1001, aiocb.fd());
47     assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode());
48     assert_eq!(4, aiocb.nbytes());
49     assert_eq!(2, aiocb.offset());
50     assert_eq!(42, aiocb.priority());
51     let sev = aiocb.sigevent().sigevent();
52     assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
53     assert_eq!(99, sev.sigev_value.sival_ptr as i64);
54 }
55 
56 // Tests AioCb.cancel.  We aren't trying to test the OS's implementation, only
57 // our bindings.  So it's sufficient to check that AioCb.cancel returned any
58 // AioCancelStat value.
59 #[test]
60 #[cfg_attr(target_env = "musl", ignore)]
test_cancel()61 fn test_cancel() {
62     let wbuf: &[u8] = b"CDEF";
63 
64     let f = tempfile().unwrap();
65     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
66                             0,   //offset
67                             wbuf,
68                             0,   //priority
69                             SigevNotify::SigevNone,
70                             LioOpcode::LIO_NOP);
71     aiocb.write().unwrap();
72     let err = aiocb.error();
73     assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
74 
75     let cancelstat = aiocb.cancel();
76     assert!(cancelstat.is_ok());
77 
78     // Wait for aiocb to complete, but don't care whether it succeeded
79     let _ = poll_aio(&mut aiocb);
80     let _ = aiocb.aio_return();
81 }
82 
83 // Tests using aio_cancel_all for all outstanding IOs.
84 #[test]
85 #[cfg_attr(target_env = "musl", ignore)]
test_aio_cancel_all()86 fn test_aio_cancel_all() {
87     let wbuf: &[u8] = b"CDEF";
88 
89     let f = tempfile().unwrap();
90     let mut aiocb = AioCb::from_slice(f.as_raw_fd(),
91                             0,   //offset
92                             wbuf,
93                             0,   //priority
94                             SigevNotify::SigevNone,
95                             LioOpcode::LIO_NOP);
96     aiocb.write().unwrap();
97     let err = aiocb.error();
98     assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
99 
100     let cancelstat = aio_cancel_all(f.as_raw_fd());
101     assert!(cancelstat.is_ok());
102 
103     // Wait for aiocb to complete, but don't care whether it succeeded
104     let _ = poll_aio(&mut aiocb);
105     let _ = aiocb.aio_return();
106 }
107 
108 #[test]
109 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_fsync()110 fn test_fsync() {
111     const INITIAL: &[u8] = b"abcdef123456";
112     let mut f = tempfile().unwrap();
113     f.write_all(INITIAL).unwrap();
114     let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
115                             0,   //priority
116                             SigevNotify::SigevNone);
117     let err = aiocb.fsync(AioFsyncMode::O_SYNC);
118     assert!(err.is_ok());
119     poll_aio(&mut aiocb).unwrap();
120     aiocb.aio_return().unwrap();
121 }
122 
123 /// `AioCb::fsync` should not modify the `AioCb` object if `libc::aio_fsync` returns
124 /// an error
125 // Skip on Linux, because Linux's AIO implementation can't detect errors
126 // synchronously
127 #[test]
128 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
test_fsync_error()129 fn test_fsync_error() {
130     use std::mem;
131 
132     const INITIAL: &[u8] = b"abcdef123456";
133     // Create an invalid AioFsyncMode
134     let mode = unsafe { mem::transmute(666) };
135     let mut f = tempfile().unwrap();
136     f.write_all(INITIAL).unwrap();
137     let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
138                             0,   //priority
139                             SigevNotify::SigevNone);
140     let err = aiocb.fsync(mode);
141     assert!(err.is_err());
142 }
143 
144 #[test]
145 // On Cirrus on Linux, this test fails due to a glibc bug.
146 // https://github.com/nix-rust/nix/issues/1099
147 #[cfg_attr(target_os = "linux", ignore)]
148 // On Cirrus, aio_suspend is failing with EINVAL
149 // https://github.com/nix-rust/nix/issues/1361
150 #[cfg_attr(target_os = "macos", ignore)]
test_aio_suspend()151 fn test_aio_suspend() {
152     const INITIAL: &[u8] = b"abcdef123456";
153     const WBUF: &[u8] = b"CDEFG";
154     let timeout = TimeSpec::seconds(10);
155     let mut rbuf = vec![0; 4];
156     let rlen = rbuf.len();
157     let mut f = tempfile().unwrap();
158     f.write_all(INITIAL).unwrap();
159 
160     let mut wcb = AioCb::from_slice( f.as_raw_fd(),
161                            2,   //offset
162                            WBUF,
163                            0,   //priority
164                            SigevNotify::SigevNone,
165                            LioOpcode::LIO_WRITE);
166 
167     let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
168                             8,   //offset
169                             &mut rbuf,
170                             0,   //priority
171                             SigevNotify::SigevNone,
172                             LioOpcode::LIO_READ);
173     wcb.write().unwrap();
174     rcb.read().unwrap();
175     loop {
176         {
177             let cbbuf = [wcb.as_ref(), rcb.as_ref()];
178             let r = aio_suspend(&cbbuf[..], Some(timeout));
179             match r {
180                 Err(Errno::EINTR) => continue,
181                 Err(e) => panic!("aio_suspend returned {:?}", e),
182                 Ok(_) => ()
183             };
184         }
185         if rcb.error() != Err(Errno::EINPROGRESS) &&
186            wcb.error() != Err(Errno::EINPROGRESS) {
187             break
188         }
189     }
190 
191     assert_eq!(wcb.aio_return().unwrap() as usize, WBUF.len());
192     assert_eq!(rcb.aio_return().unwrap() as usize, rlen);
193 }
194 
195 // Test a simple aio operation with no completion notification.  We must poll
196 // for completion
197 #[test]
198 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_read()199 fn test_read() {
200     const INITIAL: &[u8] = b"abcdef123456";
201     let mut rbuf = vec![0; 4];
202     const EXPECT: &[u8] = b"cdef";
203     let mut f = tempfile().unwrap();
204     f.write_all(INITIAL).unwrap();
205     {
206         let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
207                                2,   //offset
208                                &mut rbuf,
209                                0,   //priority
210                                SigevNotify::SigevNone,
211                                LioOpcode::LIO_NOP);
212         aiocb.read().unwrap();
213 
214         let err = poll_aio(&mut aiocb);
215         assert_eq!(err, Ok(()));
216         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
217     }
218 
219     assert_eq!(EXPECT, rbuf.deref().deref());
220 }
221 
222 /// `AioCb::read` should not modify the `AioCb` object if `libc::aio_read`
223 /// returns an error
224 // Skip on Linux, because Linux's AIO implementation can't detect errors
225 // synchronously
226 #[test]
227 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
test_read_error()228 fn test_read_error() {
229     const INITIAL: &[u8] = b"abcdef123456";
230     let mut rbuf = vec![0; 4];
231     let mut f = tempfile().unwrap();
232     f.write_all(INITIAL).unwrap();
233     let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
234                            -1,   //an invalid offset
235                            &mut rbuf,
236                            0,   //priority
237                            SigevNotify::SigevNone,
238                            LioOpcode::LIO_NOP);
239     assert!(aiocb.read().is_err());
240 }
241 
242 // Tests from_mut_slice
243 #[test]
244 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_read_into_mut_slice()245 fn test_read_into_mut_slice() {
246     const INITIAL: &[u8] = b"abcdef123456";
247     let mut rbuf = vec![0; 4];
248     const EXPECT: &[u8] = b"cdef";
249     let mut f = tempfile().unwrap();
250     f.write_all(INITIAL).unwrap();
251     {
252         let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
253                                2,   //offset
254                                &mut rbuf,
255                                0,   //priority
256                                SigevNotify::SigevNone,
257                                LioOpcode::LIO_NOP);
258         aiocb.read().unwrap();
259 
260         let err = poll_aio(&mut aiocb);
261         assert_eq!(err, Ok(()));
262         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
263     }
264 
265     assert_eq!(rbuf, EXPECT);
266 }
267 
268 // Tests from_ptr
269 #[test]
270 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_read_into_pointer()271 fn test_read_into_pointer() {
272     const INITIAL: &[u8] = b"abcdef123456";
273     let mut rbuf = vec![0; 4];
274     const EXPECT: &[u8] = b"cdef";
275     let mut f = tempfile().unwrap();
276     f.write_all(INITIAL).unwrap();
277     {
278         // Safety: ok because rbuf lives until after poll_aio
279         let mut aiocb = unsafe {
280             AioCb::from_mut_ptr( f.as_raw_fd(),
281                                  2,   //offset
282                                  rbuf.as_mut_ptr() as *mut c_void,
283                                  rbuf.len(),
284                                  0,   //priority
285                                  SigevNotify::SigevNone,
286                                  LioOpcode::LIO_NOP)
287         };
288         aiocb.read().unwrap();
289 
290         let err = poll_aio(&mut aiocb);
291         assert_eq!(err, Ok(()));
292         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
293     }
294 
295     assert_eq!(rbuf, EXPECT);
296 }
297 
298 // Test reading into an immutable buffer.  It should fail
299 // FIXME: This test fails to panic on Linux/musl
300 #[test]
301 #[should_panic(expected = "Can't read into an immutable buffer")]
302 #[cfg_attr(target_env = "musl", ignore)]
test_read_immutable_buffer()303 fn test_read_immutable_buffer() {
304     let rbuf: &[u8] = b"CDEF";
305     let f = tempfile().unwrap();
306     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
307                            2,   //offset
308                            rbuf,
309                            0,   //priority
310                            SigevNotify::SigevNone,
311                            LioOpcode::LIO_NOP);
312     aiocb.read().unwrap();
313 }
314 
315 
316 // Test a simple aio operation with no completion notification.  We must poll
317 // for completion.  Unlike test_aio_read, this test uses AioCb::from_slice
318 #[test]
319 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_write()320 fn test_write() {
321     const INITIAL: &[u8] = b"abcdef123456";
322     let wbuf = "CDEF".to_string().into_bytes();
323     let mut rbuf = Vec::new();
324     const EXPECT: &[u8] = b"abCDEF123456";
325 
326     let mut f = tempfile().unwrap();
327     f.write_all(INITIAL).unwrap();
328     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
329                            2,   //offset
330                            &wbuf,
331                            0,   //priority
332                            SigevNotify::SigevNone,
333                            LioOpcode::LIO_NOP);
334     aiocb.write().unwrap();
335 
336     let err = poll_aio(&mut aiocb);
337     assert_eq!(err, Ok(()));
338     assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
339 
340     f.seek(SeekFrom::Start(0)).unwrap();
341     let len = f.read_to_end(&mut rbuf).unwrap();
342     assert_eq!(len, EXPECT.len());
343     assert_eq!(rbuf, EXPECT);
344 }
345 
346 // Tests `AioCb::from_ptr`
347 #[test]
348 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_write_from_pointer()349 fn test_write_from_pointer() {
350     const INITIAL: &[u8] = b"abcdef123456";
351     let wbuf = "CDEF".to_string().into_bytes();
352     let mut rbuf = Vec::new();
353     const EXPECT: &[u8] = b"abCDEF123456";
354 
355     let mut f = tempfile().unwrap();
356     f.write_all(INITIAL).unwrap();
357     // Safety: ok because aiocb outlives poll_aio
358     let mut aiocb = unsafe {
359         AioCb::from_ptr( f.as_raw_fd(),
360                          2,   //offset
361                          wbuf.as_ptr() as *const c_void,
362                          wbuf.len(),
363                          0,   //priority
364                          SigevNotify::SigevNone,
365                          LioOpcode::LIO_NOP)
366     };
367     aiocb.write().unwrap();
368 
369     let err = poll_aio(&mut aiocb);
370     assert_eq!(err, Ok(()));
371     assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
372 
373     f.seek(SeekFrom::Start(0)).unwrap();
374     let len = f.read_to_end(&mut rbuf).unwrap();
375     assert_eq!(len, EXPECT.len());
376     assert_eq!(rbuf, EXPECT);
377 }
378 
379 /// `AioCb::write` should not modify the `AioCb` object if `libc::aio_write`
380 /// returns an error
381 // Skip on Linux, because Linux's AIO implementation can't detect errors
382 // synchronously
383 #[test]
384 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
test_write_error()385 fn test_write_error() {
386     let wbuf = "CDEF".to_string().into_bytes();
387     let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor
388                            0,   //offset
389                            &wbuf,
390                            0,   //priority
391                            SigevNotify::SigevNone,
392                            LioOpcode::LIO_NOP);
393     assert!(aiocb.write().is_err());
394 }
395 
396 lazy_static! {
397     pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
398 }
399 
sigfunc(_: c_int)400 extern fn sigfunc(_: c_int) {
401     SIGNALED.store(true, Ordering::Relaxed);
402 }
403 
404 // Test an aio operation with completion delivered by a signal
405 // FIXME: This test is ignored on mips because of failures in qemu in CI
406 #[test]
407 #[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)]
test_write_sigev_signal()408 fn test_write_sigev_signal() {
409     let _m = crate::SIGNAL_MTX.lock();
410     let sa = SigAction::new(SigHandler::Handler(sigfunc),
411                             SaFlags::SA_RESETHAND,
412                             SigSet::empty());
413     SIGNALED.store(false, Ordering::Relaxed);
414     unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
415 
416     const INITIAL: &[u8] = b"abcdef123456";
417     const WBUF: &[u8] = b"CDEF";
418     let mut rbuf = Vec::new();
419     const EXPECT: &[u8] = b"abCDEF123456";
420 
421     let mut f = tempfile().unwrap();
422     f.write_all(INITIAL).unwrap();
423     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
424                            2,   //offset
425                            WBUF,
426                            0,   //priority
427                            SigevNotify::SigevSignal {
428                                signal: Signal::SIGUSR2,
429                                si_value: 0  //TODO: validate in sigfunc
430                            },
431                            LioOpcode::LIO_NOP);
432     aiocb.write().unwrap();
433     while !SIGNALED.load(Ordering::Relaxed) {
434         thread::sleep(time::Duration::from_millis(10));
435     }
436 
437     assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
438     f.seek(SeekFrom::Start(0)).unwrap();
439     let len = f.read_to_end(&mut rbuf).unwrap();
440     assert_eq!(len, EXPECT.len());
441     assert_eq!(rbuf, EXPECT);
442 }
443 
444 // Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the
445 // time listio returns.
446 #[test]
447 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
448 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_liocb_listio_wait()449 fn test_liocb_listio_wait() {
450     const INITIAL: &[u8] = b"abcdef123456";
451     const WBUF: &[u8] = b"CDEF";
452     let mut rbuf = vec![0; 4];
453     let rlen = rbuf.len();
454     let mut rbuf2 = Vec::new();
455     const EXPECT: &[u8] = b"abCDEF123456";
456     let mut f = tempfile().unwrap();
457 
458     f.write_all(INITIAL).unwrap();
459 
460     {
461         let mut liocb = LioCbBuilder::with_capacity(2)
462             .emplace_slice(
463                 f.as_raw_fd(),
464                 2,   //offset
465                 WBUF,
466                 0,   //priority
467                 SigevNotify::SigevNone,
468                 LioOpcode::LIO_WRITE
469             ).emplace_mut_slice(
470                 f.as_raw_fd(),
471                 8,   //offset
472                 &mut rbuf,
473                 0,   //priority
474                 SigevNotify::SigevNone,
475                 LioOpcode::LIO_READ
476             ).finish();
477         let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
478         err.expect("lio_listio");
479 
480         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
481         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
482     }
483     assert_eq!(rbuf.deref().deref(), b"3456");
484 
485     f.seek(SeekFrom::Start(0)).unwrap();
486     let len = f.read_to_end(&mut rbuf2).unwrap();
487     assert_eq!(len, EXPECT.len());
488     assert_eq!(rbuf2, EXPECT);
489 }
490 
491 // Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other
492 // mechanism to check for the individual AioCb's completion.
493 #[test]
494 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
495 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
test_liocb_listio_nowait()496 fn test_liocb_listio_nowait() {
497     const INITIAL: &[u8] = b"abcdef123456";
498     const WBUF: &[u8] = b"CDEF";
499     let mut rbuf = vec![0; 4];
500     let rlen = rbuf.len();
501     let mut rbuf2 = Vec::new();
502     const EXPECT: &[u8] = b"abCDEF123456";
503     let mut f = tempfile().unwrap();
504 
505     f.write_all(INITIAL).unwrap();
506 
507     {
508         let mut liocb = LioCbBuilder::with_capacity(2)
509             .emplace_slice(
510                 f.as_raw_fd(),
511                 2,   //offset
512                 WBUF,
513                 0,   //priority
514                 SigevNotify::SigevNone,
515                 LioOpcode::LIO_WRITE
516             ).emplace_mut_slice(
517                 f.as_raw_fd(),
518                 8,   //offset
519                 &mut rbuf,
520                 0,   //priority
521                 SigevNotify::SigevNone,
522                 LioOpcode::LIO_READ
523             ).finish();
524         let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
525         err.expect("lio_listio");
526 
527         poll_lio(&mut liocb, 0).unwrap();
528         poll_lio(&mut liocb, 1).unwrap();
529         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
530         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
531     }
532     assert_eq!(rbuf.deref().deref(), b"3456");
533 
534     f.seek(SeekFrom::Start(0)).unwrap();
535     let len = f.read_to_end(&mut rbuf2).unwrap();
536     assert_eq!(len, EXPECT.len());
537     assert_eq!(rbuf2, EXPECT);
538 }
539 
540 // Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all
541 // AioCb's are complete.
542 // FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI.
543 #[test]
544 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
545 #[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)]
test_liocb_listio_signal()546 fn test_liocb_listio_signal() {
547     let _m = crate::SIGNAL_MTX.lock();
548     const INITIAL: &[u8] = b"abcdef123456";
549     const WBUF: &[u8] = b"CDEF";
550     let mut rbuf = vec![0; 4];
551     let rlen = rbuf.len();
552     let mut rbuf2 = Vec::new();
553     const EXPECT: &[u8] = b"abCDEF123456";
554     let mut f = tempfile().unwrap();
555     let sa = SigAction::new(SigHandler::Handler(sigfunc),
556                             SaFlags::SA_RESETHAND,
557                             SigSet::empty());
558     let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2,
559                                                   si_value: 0 };
560 
561     f.write_all(INITIAL).unwrap();
562 
563     {
564         let mut liocb = LioCbBuilder::with_capacity(2)
565             .emplace_slice(
566                 f.as_raw_fd(),
567                 2,   //offset
568                 WBUF,
569                 0,   //priority
570                 SigevNotify::SigevNone,
571                 LioOpcode::LIO_WRITE
572             ).emplace_mut_slice(
573                 f.as_raw_fd(),
574                 8,   //offset
575                 &mut rbuf,
576                 0,   //priority
577                 SigevNotify::SigevNone,
578                 LioOpcode::LIO_READ
579             ).finish();
580         SIGNALED.store(false, Ordering::Relaxed);
581         unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
582         let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify);
583         err.expect("lio_listio");
584         while !SIGNALED.load(Ordering::Relaxed) {
585             thread::sleep(time::Duration::from_millis(10));
586         }
587 
588         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
589         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
590     }
591     assert_eq!(rbuf.deref().deref(), b"3456");
592 
593     f.seek(SeekFrom::Start(0)).unwrap();
594     let len = f.read_to_end(&mut rbuf2).unwrap();
595     assert_eq!(len, EXPECT.len());
596     assert_eq!(rbuf2, EXPECT);
597 }
598 
599 // Try to use LioCb::listio to read into an immutable buffer.  It should fail
600 // FIXME: This test fails to panic on Linux/musl
601 #[test]
602 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
603 #[should_panic(expected = "Can't read into an immutable buffer")]
604 #[cfg_attr(target_env = "musl", ignore)]
test_liocb_listio_read_immutable()605 fn test_liocb_listio_read_immutable() {
606     let rbuf: &[u8] = b"abcd";
607     let f = tempfile().unwrap();
608 
609 
610     let mut liocb = LioCbBuilder::with_capacity(1)
611         .emplace_slice(
612             f.as_raw_fd(),
613             2,   //offset
614             rbuf,
615             0,   //priority
616             SigevNotify::SigevNone,
617             LioOpcode::LIO_READ
618         ).finish();
619     let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
620 }
621