use crate::error::{Error, Result};
use crate::ffi;
pub struct Request {
handle: i64,
completed: bool,
}
impl Request {
pub(crate) fn new(handle: i64) -> Self {
Request {
handle,
completed: false,
}
}
pub fn raw_handle(&self) -> i64 {
self.handle
}
pub fn is_completed(&self) -> bool {
self.completed
}
pub fn wait(mut self) -> Result<()> {
if self.completed {
return Ok(());
}
let ret = unsafe { ffi::ferrompi_wait(self.handle) };
Error::check_with_op(ret, "wait")?;
self.completed = true;
Ok(())
}
pub fn test(&mut self) -> Result<bool> {
if self.completed {
return Ok(true);
}
let mut flag: i32 = 0;
let ret = unsafe { ffi::ferrompi_test(self.handle, &mut flag) };
Error::check_with_op(ret, "test")?;
if flag != 0 {
self.completed = true;
}
Ok(flag != 0)
}
pub fn wait_any(requests: &mut [Request]) -> Result<Option<usize>> {
if requests.is_empty() {
return Ok(None);
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let mut index: i32 = 0;
let ret = unsafe {
ffi::ferrompi_waitany(handles.len() as i64, handles.as_mut_ptr(), &mut index)
};
Error::check_with_op(ret, "waitany")?;
if index < 0 {
return Ok(None);
}
let idx = index as usize;
requests[idx].completed = true;
Ok(Some(idx))
}
pub fn wait_some(requests: &mut [Request]) -> Result<Vec<usize>> {
if requests.is_empty() {
return Ok(vec![]);
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let mut outcount: i64 = 0;
let mut indices: Vec<i32> = vec![0; requests.len()];
let ret = unsafe {
ffi::ferrompi_waitsome(
handles.len() as i64,
handles.as_mut_ptr(),
&mut outcount,
indices.as_mut_ptr(),
)
};
Error::check_with_op(ret, "waitsome")?;
if outcount <= 0 {
return Ok(vec![]);
}
let completed: Vec<usize> = indices[..outcount as usize]
.iter()
.map(|&i| i as usize)
.collect();
for &idx in &completed {
requests[idx].completed = true;
}
Ok(completed)
}
pub fn test_any(requests: &mut [Request]) -> Result<Option<usize>> {
if requests.is_empty() {
return Ok(None);
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let mut index: i32 = 0;
let mut flag: i32 = 0;
let ret = unsafe {
ffi::ferrompi_testany(
handles.len() as i64,
handles.as_mut_ptr(),
&mut index,
&mut flag,
)
};
Error::check_with_op(ret, "testany")?;
if flag == 0 {
return Ok(None);
}
if index < 0 {
return Ok(None);
}
let idx = index as usize;
requests[idx].completed = true;
Ok(Some(idx))
}
pub fn test_some(requests: &mut [Request]) -> Result<Vec<usize>> {
if requests.is_empty() {
return Ok(vec![]);
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let mut outcount: i64 = 0;
let mut indices: Vec<i32> = vec![0; requests.len()];
let ret = unsafe {
ffi::ferrompi_testsome(
handles.len() as i64,
handles.as_mut_ptr(),
&mut outcount,
indices.as_mut_ptr(),
)
};
Error::check_with_op(ret, "testsome")?;
if outcount <= 0 {
return Ok(vec![]);
}
let completed: Vec<usize> = indices[..outcount as usize]
.iter()
.map(|&i| i as usize)
.collect();
for &idx in &completed {
requests[idx].completed = true;
}
Ok(completed)
}
pub fn get_status(&self) -> Result<bool> {
if self.completed {
return Ok(true);
}
let mut flag: i32 = 0;
let ret = unsafe { ffi::ferrompi_request_get_status(self.handle, &mut flag) };
Error::check_with_op(ret, "request_get_status")?;
Ok(flag != 0)
}
pub fn cancel(&mut self) -> Result<()> {
if self.completed {
return Ok(());
}
let ret = unsafe { ffi::ferrompi_cancel(self.handle) };
Error::check_with_op(ret, "cancel")
}
pub fn wait_all(requests: Vec<Request>) -> Result<()> {
if requests.is_empty() {
return Ok(());
}
let mut handles: Vec<i64> = requests.iter().map(|r| r.handle).collect();
let ret = unsafe { ffi::ferrompi_waitall(handles.len() as i64, handles.as_mut_ptr()) };
if ret == 0 {
for mut req in requests {
req.completed = true;
std::mem::forget(req);
}
Ok(())
} else {
Err(Error::from_code_with_op(ret, "waitall"))
}
}
}
impl Drop for Request {
fn drop(&mut self) {
if !self.completed {
unsafe { ffi::ferrompi_wait(self.handle) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::forget;
#[test]
fn new_request_is_not_completed() {
let req = Request::new(0);
assert!(!req.is_completed());
assert_eq!(req.raw_handle(), 0);
forget(req);
}
#[test]
fn raw_handle_returns_constructor_value() {
let req = Request::new(99);
assert_eq!(req.raw_handle(), 99);
forget(req);
}
#[test]
fn test_when_already_completed_returns_true() {
let mut req = Request {
handle: 0,
completed: true,
};
let result = req.test();
assert!(matches!(result, Ok(true)));
forget(req);
}
#[test]
fn wait_when_already_completed_returns_ok() {
let req = Request {
handle: 0,
completed: true,
};
let result = req.wait();
assert!(result.is_ok());
}
#[test]
fn wait_all_empty_vec_returns_ok() {
let result = Request::wait_all(vec![]);
assert!(result.is_ok());
}
#[test]
fn wait_any_empty_vec_returns_none() {
let mut v: Vec<Request> = vec![];
assert_eq!(Request::wait_any(&mut v).unwrap(), None);
}
#[test]
fn wait_some_empty_vec_returns_empty() {
let mut v: Vec<Request> = vec![];
assert_eq!(Request::wait_some(&mut v).unwrap(), Vec::<usize>::new());
}
#[test]
fn test_any_empty_vec_returns_none() {
let mut v: Vec<Request> = vec![];
assert_eq!(Request::test_any(&mut v).unwrap(), None);
}
#[test]
fn test_some_empty_vec_returns_empty() {
let mut v: Vec<Request> = vec![];
assert_eq!(Request::test_some(&mut v).unwrap(), Vec::<usize>::new());
}
#[test]
fn get_status_on_completed_request_returns_true_without_ffi() {
let req = Request {
handle: 0,
completed: true,
};
let result = req.get_status();
assert!(matches!(result, Ok(true)));
forget(req);
}
#[test]
fn cancel_on_completed_request_returns_ok_without_ffi() {
let mut req = Request {
handle: 0,
completed: true,
};
let result = req.cancel();
assert!(matches!(result, Ok(())));
forget(req);
}
}