Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  test_aio.rs   Sprache: unbekannt

 
use std::{
    io::{Read, Seek, Write},
    ops::Deref,
    os::unix::io::{AsFd, AsRawFd, BorrowedFd},
    pin::Pin,
    sync::atomic::{AtomicBool, Ordering},
    thread, time,
};

use libc::c_int;
use nix::{
    errno::*,
    sys::{
        aio::*,
        signal::{
            sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
            Signal,
        },
        time::{TimeSpec, TimeValLike},
    },
};
use tempfile::tempfile;

pub static SIGNALED: AtomicBool = AtomicBool::new(false);

extern "C" fn sigfunc(_: c_int) {
    SIGNALED.store(true, Ordering::Relaxed);
}

// Helper that polls an AioCb for completion or error
macro_rules! poll_aio {
    ($aiocb: expr) => {
        loop {
            let err = $aiocb.as_mut().error();
            if err != Err(Errno::EINPROGRESS) {
                break err;
            };
            thread::sleep(time::Duration::from_millis(10));
        }
    };
}

mod aio_fsync {
    use super::*;

    #[test]
    fn test_accessors() {
        let f = tempfile().unwrap();
        let aiocb = AioFsync::new(
            f.as_fd(),
            AioFsyncMode::O_SYNC,
            42,
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 99,
            },
        );
        assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
        assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
        assert_eq!(42, aiocb.priority());
        let sev = aiocb.sigevent().sigevent();
        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
    }

    /// `AioFsync::submit` should not modify the `AioCb` object if
    /// `libc::aio_fsync` returns an error
    // Skip on Linux, because Linux's AIO implementation can't detect errors
    // synchronously
    #[test]
    #[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
    fn error() {
        use std::mem;

        const INITIAL: &[u8] = b"abcdef123456";
        // Create an invalid AioFsyncMode
        let mode = unsafe { mem::transmute::<i32, AioFsyncMode>(666) };
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        let mut aiof =
            Box::pin(AioFsync::new(f.as_fd(), mode, 0, SigevNotify::SigevNone));
        let err = aiof.as_mut().submit();
        err.expect_err("assertion failed");
    }

    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn ok() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        let mut aiof = Box::pin(AioFsync::new(
            f.as_fd(),
            AioFsyncMode::O_SYNC,
            0,
            SigevNotify::SigevNone,
        ));
        aiof.as_mut().submit().unwrap();
        poll_aio!(&mut aiof).unwrap();
        aiof.as_mut().aio_return().unwrap();
    }
}

mod aio_read {
    use super::*;

    #[test]
    fn test_accessors() {
        let f = tempfile().unwrap();
        let mut rbuf = vec![0; 4];
        let aiocb = AioRead::new(
            f.as_fd(),
            2, //offset
            &mut rbuf,
            42, //priority
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 99,
            },
        );
        assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
        assert_eq!(4, aiocb.nbytes());
        assert_eq!(2, aiocb.offset());
        assert_eq!(42, aiocb.priority());
        let sev = aiocb.sigevent().sigevent();
        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
    }

    // Tests AioWrite.cancel.  We aren't trying to test the OS's implementation,
    // only our bindings.  So it's sufficient to check that cancel
    // returned any AioCancelStat value.
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn cancel() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut rbuf = vec![0; 4];
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        let fd = f.as_fd();
        let mut aior =
            Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
        aior.as_mut().submit().unwrap();

        aior.as_mut().cancel().unwrap();

        // Wait for aiow to complete, but don't care whether it succeeded
        let _ = poll_aio!(&mut aior);
        let _ = aior.as_mut().aio_return();
    }

    /// `AioRead::submit` should not modify the `AioCb` object if
    /// `libc::aio_read` returns an error
    // Skip on Linux, because Linux's AIO implementation can't detect errors
    // synchronously
    #[test]
    #[cfg(any(target_os = "freebsd", apple_targets))]
    fn error() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut rbuf = vec![0; 4];
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        let mut aior = Box::pin(AioRead::new(
            f.as_fd(),
            -1, //an invalid offset
            &mut rbuf,
            0, //priority
            SigevNotify::SigevNone,
        ));
        aior.as_mut().submit().expect_err("assertion failed");
    }

    // Test a simple aio operation with no completion notification.  We must
    // poll for completion
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn ok() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut rbuf = vec![0; 4];
        const EXPECT: &[u8] = b"cdef";
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let fd = f.as_fd();
            let mut aior = Box::pin(AioRead::new(
                fd,
                2,
                &mut rbuf,
                0,
                SigevNotify::SigevNone,
            ));
            aior.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aior);
            assert_eq!(err, Ok(()));
            assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
        }
        assert_eq!(EXPECT, rbuf.deref());
    }

    // Like ok, but allocates the structure on the stack.
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn on_stack() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut rbuf = vec![0; 4];
        const EXPECT: &[u8] = b"cdef";
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let fd = f.as_fd();
            let mut aior =
                AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
            let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
            aior.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aior);
            assert_eq!(err, Ok(()));
            assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
        }
        assert_eq!(EXPECT, rbuf.deref());
    }
}

#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_readv {
    use std::io::IoSliceMut;

    use super::*;

    #[test]
    fn test_accessors() {
        let f = tempfile().unwrap();
        let mut rbuf0 = vec![0; 4];
        let mut rbuf1 = vec![0; 8];
        let mut rbufs =
            [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
        let aiocb = AioReadv::new(
            f.as_fd(),
            2, //offset
            &mut rbufs,
            42, //priority
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 99,
            },
        );
        assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
        assert_eq!(2, aiocb.iovlen());
        assert_eq!(2, aiocb.offset());
        assert_eq!(42, aiocb.priority());
        let sev = aiocb.sigevent().sigevent();
        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
    }

    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn ok() {
        const INITIAL: &[u8] = b"abcdef123456";
        let mut rbuf0 = vec![0; 4];
        let mut rbuf1 = vec![0; 2];
        let mut rbufs =
            [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
        const EXPECT0: &[u8] = b"cdef";
        const EXPECT1: &[u8] = b"12";
        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let fd = f.as_fd();
            let mut aior = Box::pin(AioReadv::new(
                fd,
                2,
                &mut rbufs,
                0,
                SigevNotify::SigevNone,
            ));
            aior.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aior);
            assert_eq!(err, Ok(()));
            assert_eq!(
                aior.as_mut().aio_return().unwrap(),
                EXPECT0.len() + EXPECT1.len()
            );
        }
        assert_eq!(&EXPECT0, &rbuf0);
        assert_eq!(&EXPECT1, &rbuf1);
    }
}

mod aio_write {
    use super::*;

    #[test]
    fn test_accessors() {
        let f = tempfile().unwrap();
        let wbuf = vec![0; 4];
        let aiocb = AioWrite::new(
            f.as_fd(),
            2, //offset
            &wbuf,
            42, //priority
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 99,
            },
        );
        assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
        assert_eq!(4, aiocb.nbytes());
        assert_eq!(2, aiocb.offset());
        assert_eq!(42, aiocb.priority());
        let sev = aiocb.sigevent().sigevent();
        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
    }

    // Tests AioWrite.cancel.  We aren't trying to test the OS's implementation,
    // only our bindings.  So it's sufficient to check that cancel
    // returned any AioCancelStat value.
    #[test]
    #[cfg_attr(target_env = "musl", ignore)]
    fn cancel() {
        let wbuf: &[u8] = b"CDEF";

        let f = tempfile().unwrap();
        let mut aiow = Box::pin(AioWrite::new(
            f.as_fd(),
            0,
            wbuf,
            0,
            SigevNotify::SigevNone,
        ));
        aiow.as_mut().submit().unwrap();
        let err = aiow.as_mut().error();
        assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));

        aiow.as_mut().cancel().unwrap();

        // Wait for aiow to complete, but don't care whether it succeeded
        let _ = poll_aio!(&mut aiow);
        let _ = aiow.as_mut().aio_return();
    }

    // Test a simple aio operation with no completion notification.  We must
    // poll for completion.
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn ok() {
        const INITIAL: &[u8] = b"abcdef123456";
        let wbuf = "CDEF".to_string().into_bytes();
        let mut rbuf = Vec::new();
        const EXPECT: &[u8] = b"abCDEF123456";

        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let mut aiow = Box::pin(AioWrite::new(
                f.as_fd(),
                2,
                &wbuf,
                0,
                SigevNotify::SigevNone,
            ));
            aiow.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aiow);
            assert_eq!(err, Ok(()));
            assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
        }

        f.rewind().unwrap();
        let len = f.read_to_end(&mut rbuf).unwrap();
        assert_eq!(len, EXPECT.len());
        assert_eq!(rbuf, EXPECT);
    }

    // Like ok, but allocates the structure on the stack.
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn on_stack() {
        const INITIAL: &[u8] = b"abcdef123456";
        let wbuf = "CDEF".to_string().into_bytes();
        let mut rbuf = Vec::new();
        const EXPECT: &[u8] = b"abCDEF123456";

        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let mut aiow = AioWrite::new(
                f.as_fd(),
                2, //offset
                &wbuf,
                0, //priority
                SigevNotify::SigevNone,
            );
            let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
            aiow.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aiow);
            assert_eq!(err, Ok(()));
            assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
        }

        f.rewind().unwrap();
        let len = f.read_to_end(&mut rbuf).unwrap();
        assert_eq!(len, EXPECT.len());
        assert_eq!(rbuf, EXPECT);
    }

    /// `AioWrite::write` should not modify the `AioCb` object if
    /// `libc::aio_write` returns an error.
    // Skip on Linux, because Linux's AIO implementation can't detect errors
    // synchronously
    #[test]
    #[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
    fn error() {
        // Not I/O safe!  Deliberately create an invalid fd.
        let fd = unsafe { BorrowedFd::borrow_raw(666) };
        let wbuf = "CDEF".to_string().into_bytes();
        let mut aiow = Box::pin(AioWrite::new(
            fd,
            0, //offset
            &wbuf,
            0, //priority
            SigevNotify::SigevNone,
        ));
        aiow.as_mut().submit().expect_err("assertion failed");
        // Dropping the AioWrite at this point should not panic
    }
}

#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_writev {
    use std::io::IoSlice;

    use super::*;

    #[test]
    fn test_accessors() {
        let f = tempfile().unwrap();
        let wbuf0 = vec![0; 4];
        let wbuf1 = vec![0; 8];
        let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
        let aiocb = AioWritev::new(
            f.as_fd(),
            2, //offset
            &wbufs,
            42, //priority
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 99,
            },
        );
        assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
        assert_eq!(2, aiocb.iovlen());
        assert_eq!(2, aiocb.offset());
        assert_eq!(42, aiocb.priority());
        let sev = aiocb.sigevent().sigevent();
        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
    }

    // Test a simple aio operation with no completion notification.  We must
    // poll for completion.
    #[test]
    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
    fn ok() {
        const INITIAL: &[u8] = b"abcdef123456";
        let wbuf0 = b"BC";
        let wbuf1 = b"DEF";
        let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
        let wlen = wbuf0.len() + wbuf1.len();
        let mut rbuf = Vec::new();
        const EXPECT: &[u8] = b"aBCDEF123456";

        let mut f = tempfile().unwrap();
        f.write_all(INITIAL).unwrap();
        {
            let mut aiow = Box::pin(AioWritev::new(
                f.as_fd(),
                1,
                &wbufs,
                0,
                SigevNotify::SigevNone,
            ));
            aiow.as_mut().submit().unwrap();

            let err = poll_aio!(&mut aiow);
            assert_eq!(err, Ok(()));
            assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
        }

        f.rewind().unwrap();
        let len = f.read_to_end(&mut rbuf).unwrap();
        assert_eq!(len, EXPECT.len());
        assert_eq!(rbuf, EXPECT);
    }
}

// Test an aio operation with completion delivered by a signal
#[test]
#[cfg_attr(
    any(
        all(target_env = "musl", target_arch = "x86_64"),
        target_arch = "mips",
        target_arch = "mips32r6",
        target_arch = "mips64",
        target_arch = "mips64r6"
    ),
    ignore
)]
fn sigev_signal() {
    let _m = crate::SIGNAL_MTX.lock();
    let sa = SigAction::new(
        SigHandler::Handler(sigfunc),
        SaFlags::SA_RESETHAND,
        SigSet::empty(),
    );
    SIGNALED.store(false, Ordering::Relaxed);
    unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();

    const INITIAL: &[u8] = b"abcdef123456";
    const WBUF: &[u8] = b"CDEF";
    let mut rbuf = Vec::new();
    const EXPECT: &[u8] = b"abCDEF123456";

    let mut f = tempfile().unwrap();
    f.write_all(INITIAL).unwrap();
    {
        let mut aiow = Box::pin(AioWrite::new(
            f.as_fd(),
            2, //offset
            WBUF,
            0, //priority
            SigevNotify::SigevSignal {
                signal: Signal::SIGUSR2,
                si_value: 0, //TODO: validate in sigfunc
            },
        ));
        aiow.as_mut().submit().unwrap();
        while !SIGNALED.load(Ordering::Relaxed) {
            thread::sleep(time::Duration::from_millis(10));
        }

        assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
    }

    f.rewind().unwrap();
    let len = f.read_to_end(&mut rbuf).unwrap();
    assert_eq!(len, EXPECT.len());
    assert_eq!(rbuf, EXPECT);
}

// Tests using aio_cancel_all for all outstanding IOs.
#[test]
#[cfg_attr(target_env = "musl", ignore)]
fn test_aio_cancel_all() {
    let wbuf: &[u8] = b"CDEF";

    let f = tempfile().unwrap();
    let mut aiocb = Box::pin(AioWrite::new(
        f.as_fd(),
        0, //offset
        wbuf,
        0, //priority
        SigevNotify::SigevNone,
    ));
    aiocb.as_mut().submit().unwrap();
    let err = aiocb.as_mut().error();
    assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));

    aio_cancel_all(f.as_fd()).unwrap();

    // Wait for aiocb to complete, but don't care whether it succeeded
    let _ = poll_aio!(&mut aiocb);
    let _ = aiocb.as_mut().aio_return();
}

#[test]
fn test_aio_suspend() {
    const INITIAL: &[u8] = b"abcdef123456";
    const WBUF: &[u8] = b"CDEFG";
    let timeout = TimeSpec::seconds(10);
    let mut rbuf = vec![0; 4];
    let rlen = rbuf.len();
    let mut f = tempfile().unwrap();
    f.write_all(INITIAL).unwrap();

    let mut wcb = Box::pin(AioWrite::new(
        f.as_fd(),
        2, //offset
        WBUF,
        0, //priority
        SigevNotify::SigevNone,
    ));

    let mut rcb = Box::pin(AioRead::new(
        f.as_fd(),
        8, //offset
        &mut rbuf,
        0, //priority
        SigevNotify::SigevNone,
    ));
    wcb.as_mut().submit().unwrap();
    rcb.as_mut().submit().unwrap();
    loop {
        {
            let cbbuf = [
                &*wcb as &dyn AsRef<libc::aiocb>,
                &*rcb as &dyn AsRef<libc::aiocb>,
            ];
            let r = aio_suspend(&cbbuf[..], Some(timeout));
            match r {
                Err(Errno::EINTR) => continue,
                Err(e) => panic!("aio_suspend returned {e:?}"),
                Ok(_) => (),
            };
        }
        if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
            && wcb.as_mut().error() != Err(Errno::EINPROGRESS)
        {
            break;
        }
    }

    assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
    assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
}

/// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
/// pointers.  This test ensures that such casts are valid.
#[test]
fn casting() {
    let sev = SigevNotify::SigevNone;
    // Only safe because we'll never await the futures
    let fd = unsafe { BorrowedFd::borrow_raw(666) };
    let aiof = AioFsync::new(fd, AioFsyncMode::O_SYNC, 0, sev);
    assert_eq!(
        aiof.as_ref() as *const libc::aiocb,
        &aiof as *const AioFsync as *const libc::aiocb
    );

    let mut rbuf = [];
    let aior = AioRead::new(fd, 0, &mut rbuf, 0, sev);
    assert_eq!(
        aior.as_ref() as *const libc::aiocb,
        &aior as *const AioRead as *const libc::aiocb
    );

    let wbuf = [];
    let aiow = AioWrite::new(fd, 0, &wbuf, 0, sev);
    assert_eq!(
        aiow.as_ref() as *const libc::aiocb,
        &aiow as *const AioWrite as *const libc::aiocb
    );
}

#[cfg(target_os = "freebsd")]
#[test]
fn casting_vectored() {
    use std::io::{IoSlice, IoSliceMut};

    let sev = SigevNotify::SigevNone;

    let mut rbuf = [];
    let mut rbufs = [IoSliceMut::new(&mut rbuf)];
    // Only safe because we'll never await the futures
    let fd = unsafe { BorrowedFd::borrow_raw(666) };
    let aiorv = AioReadv::new(fd, 0, &mut rbufs[..], 0, sev);
    assert_eq!(
        aiorv.as_ref() as *const libc::aiocb,
        &aiorv as *const AioReadv as *const libc::aiocb
    );

    let wbuf = [];
    let wbufs = [IoSlice::new(&wbuf)];
    let aiowv = AioWritev::new(fd, 0, &wbufs, 0, sev);
    assert_eq!(
        aiowv.as_ref() as *const libc::aiocb,
        &aiowv as *const AioWritev as *const libc::aiocb
    );
}

[ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge