Commit 87b671bb authored by RILLING Louis's avatar RILLING Louis
Browse files

tansiv: Make vsg_start() optionally return the time offset from simulation time

parent 7450c441
......@@ -4,4 +4,4 @@
branch = dev/waitfree
[submodule "src/qemu"]
path = src/qemu
url = https://gitlab.inria.fr/msimonin/qemu
url = https://gitlab.inria.fr/lrilling/qemu
......@@ -53,7 +53,7 @@ vsg_context* init_vsg(int argc, char* argv[])
die("Unable to initialize the context", 0);
}
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
if (ret) {
die("Unable to start the vsg client", ret);
}
......
......@@ -56,7 +56,7 @@ int main(int argc, char* argv[])
die("Unable to initialize the context", 0);
}
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
if (ret) {
die("Unable to start the vsg client", ret);
}
......
......@@ -82,6 +82,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
name = "capi"
version = "0.1.0"
dependencies = [
"chrono",
"libc",
"log",
"static_assertions",
......
......@@ -13,6 +13,7 @@ use-own-logger = ["tansiv-client/use-own-logger"]
[dependencies]
tansiv-client = {version = "0.1.0", path = "../tansiv-client"}
chrono = "0.4"
libc = "0.2"
log = "0.4"
static_assertions = "0.3.1"
......
......@@ -3,6 +3,7 @@
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#define MAX_PACKET_SIZE 2048
......@@ -14,7 +15,7 @@ struct vsg_context* vsg_init(int argc, const char* const argv[], int* next_arg_p
uintptr_t recv_cb_arg);
void vsg_cleanup(struct vsg_context* context);
int vsg_start(const struct vsg_context* context);
int vsg_start(const struct vsg_context* context, struct timespec* offset);
int vsg_stop(const struct vsg_context* context);
int vsg_gettimeofday(const struct vsg_context* context, struct timeval* timeval, void* timezone);
......
......@@ -67,11 +67,52 @@ pub unsafe extern fn vsg_cleanup(context: *const Context) {
}
}
/// Start the simulation and optionally return the time offset from simulation time to execution
/// context time.
///
/// The simulation time is assumed to start at 0 and the execution context time anchor depends on
/// the context type:
/// - for a process execution context, Time 0 refers to UNIX Epoch, that is
/// 1970/01/01 00:00;
/// - for a Qemu execution context, Time 0 is set internally in Qemu and the offset is recorded
/// when calling vsg_start().
///
/// # Safety
///
/// * `context` should point to a valid context, as previously returned by [`vsg_init`].
///
/// * `offset` may be `NULL` or should point to a valid memory area.
///
/// # Error codes
///
/// * Fails with `libc::EINVAL` whenever context is `NULL`.
///
/// * Fails with `libc::EALREADY` whenever start() has already been called on this context.
///
/// * Fails with `libc::ENOMEM` if no buffer can be allocated for vsg protocol messages.
///
/// * Fails with `libc::EPROTO` if an error occurs in the vsg protocol.
///
/// * Fails with `libc::EMSGSIZE` if message buffers were configured too short for vsg protocol
/// messages.
///
/// * Fails with `libc::EIO` if any other error happens in the low-level communication functions of
/// the vsg protocol.
#[no_mangle]
pub unsafe extern fn vsg_start(context: *const Context) -> c_int {
pub unsafe extern fn vsg_start(context: *const Context, offset: *mut libc::timespec) -> c_int {
if let Some(context) = context.as_ref() {
match (*context).start() {
Ok(_) => 0,
Ok(o) => {
let num_seconds = o.num_seconds();
let num_subsec_nanos = (o - chrono::Duration::seconds(num_seconds)).num_nanoseconds().unwrap();
if let Some(offset) = offset.as_mut() {
*offset = libc::timespec {
tv_sec: num_seconds as libc::time_t,
tv_nsec: num_subsec_nanos as libc::c_long,
};
}
0
},
Err(e) => match e {
Error::AlreadyStarted => libc::EALREADY,
Error::NoMemoryAvailable => libc::ENOMEM,
......@@ -86,6 +127,15 @@ pub unsafe extern fn vsg_start(context: *const Context) -> c_int {
}
}
/// Stop the simulation.
///
/// # Safety
///
/// * `context` should point to a valid context, as previously returned by [`vsg_init`].
///
/// # Error codes
///
/// * Fails with `libc::EINVAL` whenever context is `NULL`.
#[no_mangle]
pub unsafe extern fn vsg_stop(context: *const Context) -> c_int {
if let Some(context) = context.as_ref() {
......@@ -253,7 +303,7 @@ pub unsafe extern fn vsg_poll(context: *const Context) -> c_int {
#[cfg(test)]
mod test {
use tansiv_client::test_helpers::*;
use libc::timeval;
use libc::{timespec, timeval};
#[allow(unused_imports)]
use log::{error, info};
use std::ffi::CString;
......@@ -497,6 +547,11 @@ mod test {
unsafe { vsg_cleanup(std::ptr::null_mut()) }
}
const TIMESPEC_POISON: timespec = timespec {
tv_sec: std::i64::MAX,
tv_nsec: std::i64::MAX,
};
const TIMEVAL_POISON: timeval = timeval {
tv_sec: std::i64::MAX,
tv_usec: std::i64::MAX,
......@@ -511,8 +566,11 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let mut offset = TIMESPEC_POISON;
let res: c_int = unsafe { vsg_start(context, &mut offset) };
assert_eq!(0, res);
assert_eq!(0, offset.tv_sec);
assert_eq!(0, offset.tv_nsec);
let res: c_int = unsafe { vsg_stop(context) };
assert_eq!(0, res);
......@@ -525,10 +583,29 @@ mod test {
fn start_no_context() {
init();
let res: c_int = unsafe { vsg_start(std::ptr::null()) };
let res: c_int = unsafe { vsg_start(std::ptr::null(), std::ptr::null_mut()) };
assert_eq!(libc::EINVAL, res);
}
#[test]
fn start_no_offset() {
init();
let actor = TestActorDesc::new("titi", start_actor);
let args = valid_args!();
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let res: c_int = unsafe { vsg_stop(context) };
assert_eq!(0, res);
unsafe { vsg_cleanup(context) };
drop(actor);
}
#[test]
fn stop_no_context() {
init();
......@@ -546,7 +623,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let buffer = b"Foo msg";
......@@ -570,7 +647,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let dst = remote_vsg_address!();
......@@ -593,7 +670,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let dst = remote_vsg_address!();
......@@ -621,7 +698,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let buffer = [0u8; tansiv_client::MAX_PACKET_SIZE + 1];
......@@ -666,7 +743,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -704,7 +781,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -740,7 +817,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -776,7 +853,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -810,7 +887,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -846,7 +923,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -901,7 +978,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -939,7 +1016,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let mut src = local_vsg_address!();
......@@ -977,7 +1054,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
recv_notifier.wait(1000);
......@@ -1015,7 +1092,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), RecvNotifier::callback, RecvNotifier::get_callback_arg(&recv_notifier)) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
loop {
......@@ -1055,7 +1132,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let mut tv = TIMEVAL_POISON;
......@@ -1085,7 +1162,7 @@ mod test {
let context = unsafe { vsg_init(args.argc(), args.argv(), std::ptr::null_mut(), dummy_recv_callback, 0) };
assert!(!context.is_null());
let res: c_int = unsafe { vsg_start(context) };
let res: c_int = unsafe { vsg_start(context, std::ptr::null_mut()) };
assert_eq!(0, res);
let res: c_int = unsafe { vsg_gettimeofday(context, std::ptr::null_mut(), std::ptr::null_mut()) };
......
......@@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void recv_cb(uintptr_t arg)
{
......@@ -22,6 +23,7 @@ void die(const char* msg, int error)
int main(int argc, const char* argv[])
{
struct vsg_context* context;
struct timespec offset;
struct timeval time;
int flag = false;
unsigned char msg[] = "Foo msg";
......@@ -31,7 +33,7 @@ int main(int argc, const char* argv[])
if (!context)
die("vsg_init() failed", 0);
res = vsg_start(context);
res = vsg_start(context, &offset);
if (res)
die("vsg_start() failed", res);
......
......@@ -129,7 +129,7 @@ impl Context {
Ok(context)
}
pub fn start(&self) -> Result<()> {
pub fn start(&self) -> Result<chrono::Duration> {
let mut res = Err(Error::AlreadyStarted);
self.start_once.call_once(|| res = (|| {
......@@ -141,7 +141,10 @@ impl Context {
drop(connector);
match msg {
// Writing Ok(...?) helps the compiler to know how to convert std::io::Error to Error
MsgIn::GoToDeadline(deadline) => Ok(self.timer_context.start(deadline)?),
MsgIn::GoToDeadline(deadline) => {
(self.deadline_callback)(deadline);
Ok(self.timer_context.start(deadline)?)
},
_ => Err(Error::ProtocolViolation),
}
})());
......@@ -490,8 +493,9 @@ mod test {
let context = super::init(valid_args!(), Box::new(dummy_recv_callback))
.expect("init failed");
context.start()
let offset = context.start()
.expect("start failed");
assert_eq!(chrono::Duration::zero(), offset);
context.stop();
......@@ -733,8 +737,9 @@ mod test {
let context = super::init(valid_args_h1!(), Box::new(dummy_recv_callback))
.expect("init failed");
context.start()
let offset = context.start()
.expect("start failed");
assert_eq!(chrono::Duration::hours(1), offset);
let tv = context.gettimeofday();
// 10 seconds should be enough for slow machines...
......
......@@ -33,6 +33,9 @@ impl AdjustedTime {
#[derive(Debug)]
pub struct TimerContext {
// Offset from simulation time to application time
// Concurrency: RO
time_offset: Duration,
// Object to get and adjust the local application time
// Concurrency:
// - read by application code
......@@ -89,6 +92,8 @@ impl TimerContext {
pub(crate) fn new(config: &crate::Config) -> Result<TimerContext> {
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet};
let time_offset = config.time_offset.signed_duration_since(NaiveDateTime::from_timestamp(0, 0));
let application_time = AdjustedTime::new(Duration::zero());
let simulation_time = AdjustedTime::new(Duration::zero());
......@@ -107,6 +112,7 @@ impl TimerContext {
let next_deadline_raw = Mutex::new(StdDuration::new(0, 0));
Ok(TimerContext {
time_offset: time_offset,
application_time: application_time,
simulation_time: simulation_time,
timer_id: timer_id,
......@@ -178,13 +184,15 @@ impl TimerContext {
Ok(())
}
pub fn start(&self, deadline: StdDuration) -> Result<()> {
pub fn start(&self, deadline: StdDuration) -> Result<Duration> {
self.stopped.store(false, Ordering::Release);
let res = self.thaw_time_to_deadline(None, deadline);
if res.is_err() {
self.stopped.store(true, Ordering::Release);
match self.thaw_time_to_deadline(None, deadline) {
Ok(_) => Ok(self.time_offset),
Err(e) => {
self.stopped.store(true, Ordering::Release);
Err(e)
},
}
res
}
pub fn stop(&self) {
......
......@@ -93,7 +93,7 @@ impl TimerContextInner {
unsafe { qemu_timer_sys::timer_mod(qemu_timer, timer_deadline) };
}
pub fn start(self: &Pin<Arc<Self>>, deadline: StdDuration) -> Result<()> {
pub fn start(self: &Pin<Arc<Self>>, deadline: StdDuration) -> Result<Duration> {
// TODO: Make sure ::start() is not called again before ::stop()
// Count a new reference to self in qemu_timer
......@@ -118,11 +118,12 @@ impl TimerContextInner {
// - qemu_clock_get_ns() only accesses Qemu's internal data
// - qemu_clock_get_ns() does not require locking
let vm_time = unsafe { qemu_clock_get_ns(QEMUClockType::QEMU_CLOCK_VIRTUAL) };
*self.offset.lock().unwrap() = Duration::nanoseconds(vm_time);
let vm_time = Duration::nanoseconds(vm_time);
*self.offset.lock().unwrap() = vm_time;
self.set_next_deadline(deadline);
Ok(())
Ok(vm_time)
}
// TODO: Currently unsafe! Assumes that start() has been called before and that stop() is never
......
Subproject commit ef61a975def2e968a4e7adf98989c8e671f3f20e
Subproject commit 4521f3fd818739b4eb5513b32482d28f9a097507
......@@ -51,7 +51,7 @@ TEST_CASE("initialize the vsg client", "[vsg]")
vsg_context* context = vsg_init(argc, argv, NULL, recv_cb, 0);
REQUIRE(context != NULL);
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
REQUIRE(ret == 0);
vsg_stop(context);
......@@ -70,7 +70,7 @@ TEST_CASE("VSG receive one message", "[vsg]")
vsg_context* context = vsg_init(argc, argv, NULL, recv_cb, 0);
REQUIRE(context != NULL);
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
REQUIRE(ret == 0);
std::string msg = MESSAGE;
......@@ -94,7 +94,7 @@ TEST_CASE("VSG deliver one message", "[vsg]")
vsg_context* context = vsg_init(6, argv, NULL, recv_cb_atomic, (uintptr_t)&message_delivered);
REQUIRE(context != NULL);
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
REQUIRE(ret == 0);
// loop until our atomic is set to true
......@@ -139,7 +139,7 @@ TEST_CASE("VSG send piggyback port", "[vsg]")
vsg_context* context = vsg_init(argc, argv, NULL, recv_cb, 0);
REQUIRE(context != NULL);
int ret = vsg_start(context);
int ret = vsg_start(context, NULL);
REQUIRE(ret == 0);
in_port_t port = 5000;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment