mirror of
https://github.com/bspeice/aeron-rs
synced 2024-12-21 05:28:10 -05:00
Merge #21
21: Driver proxy r=bspeice a=bspeice Start implementing the proxy, makes interactions significantly easier. Co-authored-by: Bradlee Speice <bradlee@speice.io>
This commit is contained in:
commit
531bc0d64c
@ -1,6 +0,0 @@
|
||||
//! Aeron client
|
||||
//!
|
||||
//! These are the modules necessary to construct a functioning Aeron client
|
||||
pub mod cnc_descriptor;
|
||||
pub mod concurrent;
|
||||
pub mod context;
|
@ -77,7 +77,7 @@ pub const CNC_FILE: &str = "cnc.dat";
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::client::cnc_descriptor::{MetaDataDefinition, CNC_FILE, CNC_VERSION};
|
||||
use crate::cnc_descriptor::{MetaDataDefinition, CNC_FILE, CNC_VERSION};
|
||||
use crate::driver::DriverContext;
|
||||
use memmap::MmapOptions;
|
||||
use std::fs::File;
|
53
aeron-rs/src/command/correlated_message.rs
Normal file
53
aeron-rs/src/command/correlated_message.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! Header struct for commands that use an identifier to associate the media driver response.
|
||||
use crate::command::flyweight::Flyweight;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
|
||||
/// Basic definition for messages that include a client and correlation identifier to associate
|
||||
/// commands and responses
|
||||
#[repr(C, packed(4))]
|
||||
pub struct CorrelatedMessageDefn {
|
||||
pub(in crate::command) client_id: i64,
|
||||
pub(in crate::command) correlation_id: i64,
|
||||
}
|
||||
|
||||
impl<A> Flyweight<A, CorrelatedMessageDefn>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
/// Retrieve the client identifier associated with this message
|
||||
pub fn client_id(&self) -> i64 {
|
||||
self.get_struct().client_id
|
||||
}
|
||||
|
||||
/// Set the client identifier for this message
|
||||
pub fn put_client_id(&mut self, value: i64) -> &mut Self {
|
||||
self.get_struct_mut().client_id = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Retrieve the correlation identifier associated with this message.
|
||||
/// Will uniquely identify a command and response pair.
|
||||
pub fn correlation_id(&self) -> i64 {
|
||||
self.get_struct().correlation_id
|
||||
}
|
||||
|
||||
/// Set the correlation identifier for this message
|
||||
pub fn put_correlation_id(&mut self, value: i64) -> &mut Self {
|
||||
self.get_struct_mut().correlation_id = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::command::correlated_message::CorrelatedMessageDefn;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn correlated_message_size() {
|
||||
assert_eq!(
|
||||
size_of::<CorrelatedMessageDefn>(),
|
||||
size_of::<aeron_driver_sys::aeron_correlated_command_stct>()
|
||||
)
|
||||
}
|
||||
}
|
66
aeron-rs/src/command/flyweight.rs
Normal file
66
aeron-rs/src/command/flyweight.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! Flyweight pattern implementation for messages to and from the media driver.
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
use crate::util::{IndexT, Result};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Flyweight holder object. Wrapper around an underlying `AtomicBuffer` and
|
||||
/// offset within that buffer that all future operations are relative to.
|
||||
pub struct Flyweight<A, S>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
pub(in crate::command) buffer: A,
|
||||
base_offset: IndexT,
|
||||
_phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
/// Marker struct.
|
||||
// We can't put this `new` method in the fully generic implementation because
|
||||
// Rust gets confused as to what type `S` should be.
|
||||
pub struct Unchecked;
|
||||
|
||||
impl<A> Flyweight<A, Unchecked>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
/// Create a new flyweight object. Performs a bounds check on initialization
|
||||
/// to ensure there is space available for `S`.
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new<S>(buffer: A, offset: IndexT) -> Result<Flyweight<A, S>>
|
||||
where
|
||||
S: Sized,
|
||||
{
|
||||
buffer.overlay::<S>(offset)?;
|
||||
Ok(Flyweight {
|
||||
buffer,
|
||||
base_offset: offset,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, S> Flyweight<A, S>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
S: Sized,
|
||||
{
|
||||
pub(crate) fn get_struct(&self) -> &S {
|
||||
// UNWRAP: Bounds check performed during initialization
|
||||
self.buffer.overlay::<S>(self.base_offset).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_struct_mut(&mut self) -> &mut S {
|
||||
// UNWRAP: Bounds check performed during initialization
|
||||
self.buffer.overlay_mut::<S>(self.base_offset).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn bytes_at(&self, offset: IndexT) -> &[u8] {
|
||||
let offset = (self.base_offset + offset) as usize;
|
||||
// FIXME: Unwrap is unjustified here.
|
||||
// C++ uses pointer arithmetic with no bounds checking, so I'm more comfortable
|
||||
// with the Rust version at least panicking. Is the idea that we're safe because
|
||||
// this is a crate-local (protected in C++) method?
|
||||
self.buffer.bounds_check(offset as IndexT, 0).unwrap();
|
||||
&self.buffer[offset..]
|
||||
}
|
||||
}
|
4
aeron-rs/src/command/mod.rs
Normal file
4
aeron-rs/src/command/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Message definitions for interactions with the Media Driver
|
||||
pub mod correlated_message;
|
||||
pub mod flyweight;
|
||||
pub mod terminate_driver;
|
105
aeron-rs/src/command/terminate_driver.rs
Normal file
105
aeron-rs/src/command/terminate_driver.rs
Normal file
@ -0,0 +1,105 @@
|
||||
//! Flyweight implementation for commands to terminate the driver
|
||||
use crate::command::correlated_message::CorrelatedMessageDefn;
|
||||
use crate::command::flyweight::Flyweight;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
use crate::util::IndexT;
|
||||
use std::mem::size_of;
|
||||
|
||||
/// Raw command to terminate a driver. The `token_length` describes the length
|
||||
/// of a buffer immediately trailing this struct definition and part of the
|
||||
/// same message.
|
||||
#[repr(C, packed(4))]
|
||||
pub struct TerminateDriverDefn {
|
||||
pub(in crate::command) correlated_message: CorrelatedMessageDefn,
|
||||
pub(in crate::command) token_length: i32,
|
||||
}
|
||||
|
||||
impl<A> Flyweight<A, TerminateDriverDefn>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
/// Retrieve the client identifier of this request.
|
||||
pub fn client_id(&self) -> i64 {
|
||||
self.get_struct().correlated_message.client_id
|
||||
}
|
||||
|
||||
/// Set the client identifier of this request.
|
||||
pub fn put_client_id(&mut self, value: i64) -> &mut Self {
|
||||
self.get_struct_mut().correlated_message.client_id = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Retrieve the correlation identifier associated with this request. Used to
|
||||
/// associate driver responses with a specific request.
|
||||
pub fn correlation_id(&self) -> i64 {
|
||||
self.get_struct().correlated_message.correlation_id
|
||||
}
|
||||
|
||||
/// Set the correlation identifier to be used with this request.
|
||||
pub fn put_correlation_id(&mut self, value: i64) -> &mut Self {
|
||||
self.get_struct_mut().correlated_message.correlation_id = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the current length of the payload associated with this termination request.
|
||||
pub fn token_length(&self) -> i32 {
|
||||
self.get_struct().token_length
|
||||
}
|
||||
|
||||
/// Set the payload length of this termination request.
|
||||
///
|
||||
/// NOTE: While there are no safety issues, improperly setting this value can cause panics.
|
||||
/// The `token_length` value is automatically set during calls to `put_token_buffer()`,
|
||||
/// so this method is not likely to be frequently used.
|
||||
pub fn put_token_length(&mut self, value: i32) -> &mut Self {
|
||||
self.get_struct_mut().token_length = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Return the current token payload associated with this termination request.
|
||||
pub fn token_buffer(&self) -> &[u8] {
|
||||
// QUESTION: Should I be slicing the buffer to `token_length`?
|
||||
// C++ doesn't do anything, so I'm going to assume not.
|
||||
&self.bytes_at(size_of::<TerminateDriverDefn>() as IndexT)
|
||||
}
|
||||
|
||||
/// Append a payload to the termination request.
|
||||
pub fn put_token_buffer(&mut self, token_buffer: &[u8]) -> &mut Self {
|
||||
let token_length = token_buffer.len() as i32;
|
||||
self.get_struct_mut().token_length = token_length;
|
||||
|
||||
if token_length > 0 {
|
||||
// FIXME: Unwrap is unjustified here
|
||||
// Currently just assume that people are going to be nice about the token buffer
|
||||
// and not oversize it. C++ relies on throwing an exception if bounds are violated.
|
||||
self.buffer
|
||||
.put_slice(
|
||||
size_of::<TerminateDriverDefn>() as IndexT,
|
||||
&token_buffer,
|
||||
0,
|
||||
token_length,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the total byte length of this termination command
|
||||
pub fn length(&self) -> IndexT {
|
||||
size_of::<Self>() as IndexT + self.token_length()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::command::terminate_driver::TerminateDriverDefn;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn terminate_command_size() {
|
||||
assert_eq!(
|
||||
size_of::<TerminateDriverDefn>(),
|
||||
size_of::<aeron_driver_sys::aeron_terminate_driver_command_stct>()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! Read messages that are broadcast from the media driver; this is the primary means
|
||||
//! of receiving data.
|
||||
use crate::client::concurrent::AtomicBuffer;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
use crate::util::bit::align;
|
||||
use crate::util::{AeronError, IndexT, Result};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
@ -12,13 +12,21 @@ use std::ptr::{read_volatile, write_volatile};
|
||||
use memmap::MmapMut;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
fn bounds_check_slice(slice: &[u8], offset: IndexT, size: IndexT) -> Result<()> {
|
||||
if offset < 0 || size < 0 || slice.len() as IndexT - offset < size {
|
||||
Err(AeronError::OutOfBounds)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Atomic operations on slices of memory
|
||||
pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Check that there are at least `size` bytes of memory available
|
||||
/// beginning at some offset.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
///
|
||||
/// let buffer = &mut [0u8; 8][..];
|
||||
/// assert!(buffer.bounds_check(0, 8).is_ok());
|
||||
@ -27,11 +35,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// assert!(buffer.bounds_check(-1, 8).is_err());
|
||||
/// ```
|
||||
fn bounds_check(&self, offset: IndexT, size: IndexT) -> Result<()> {
|
||||
if offset < 0 || size < 0 || self.deref().len() as IndexT - offset < size {
|
||||
Err(AeronError::OutOfBounds)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
bounds_check_slice(self.deref(), offset, size)
|
||||
}
|
||||
|
||||
/// Overlay a struct on a buffer.
|
||||
@ -39,7 +43,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// NOTE: Has the potential to cause undefined behavior if alignment is incorrect.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// # use std::sync::atomic::{AtomicI64, Ordering};
|
||||
/// let buffer = &mut [0u8; 9][..];
|
||||
///
|
||||
@ -80,7 +84,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Overlay a struct on a buffer, and perform a volatile read
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let buffer = &mut [5, 0, 0, 0][..];
|
||||
///
|
||||
/// let my_val: u32 = buffer.overlay_volatile::<u32>(0).unwrap();
|
||||
@ -100,7 +104,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform a volatile write of a value over a buffer
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buffer = &mut [0, 0, 0, 0][..];
|
||||
///
|
||||
/// let value: u32 = 24;
|
||||
@ -121,7 +125,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform an atomic fetch and add of a 64-bit value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buf = vec![0u8; 8];
|
||||
/// assert_eq!(buf.get_and_add_i64(0, 1), Ok(0));
|
||||
/// assert_eq!(buf.get_and_add_i64(0, 1), Ok(1));
|
||||
@ -135,7 +139,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// if the update was successful, and `Ok(false)` if the update failed.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buf = &mut [0u8; 8][..];
|
||||
/// // Set value to 1
|
||||
/// buf.get_and_add_i64(0, 1).unwrap();
|
||||
@ -161,7 +165,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform a volatile read of an `i64` value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let buffer = vec![12u8, 0, 0, 0, 0, 0, 0, 0];
|
||||
/// assert_eq!(buffer.get_i64_volatile(0), Ok(12));
|
||||
/// ```
|
||||
@ -178,7 +182,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform a volatile write of an `i64` value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buffer = vec![0u8; 8];
|
||||
/// buffer.put_i64_ordered(0, 12);
|
||||
/// assert_eq!(buffer.get_i64_volatile(0), Ok(12));
|
||||
@ -190,7 +194,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Write an `i64` value into the buffer without performing any synchronization
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buffer = vec![0u8; 8];
|
||||
/// buffer.put_i64(0, 12);
|
||||
/// assert_eq!(buffer.get_i64(0), Ok(12));
|
||||
@ -199,6 +203,25 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
self.overlay_mut::<i64>(offset).map(|i| *i = value)
|
||||
}
|
||||
|
||||
/// Write the contents of a byte slice to this buffer. Does not perform any synchronization
|
||||
fn put_slice(
|
||||
&mut self,
|
||||
index: IndexT,
|
||||
source: &[u8],
|
||||
source_index: IndexT,
|
||||
len: IndexT,
|
||||
) -> Result<()> {
|
||||
self.bounds_check(index, len)?;
|
||||
bounds_check_slice(source, source_index, len)?;
|
||||
|
||||
let index = index as usize;
|
||||
let source_index = source_index as usize;
|
||||
let len = len as usize;
|
||||
|
||||
self[index..index + len].copy_from_slice(&source[source_index..source_index + len]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the contents of one buffer to another. Does not perform any synchronization
|
||||
fn put_bytes<B>(
|
||||
&mut self,
|
||||
@ -233,7 +256,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform a volatile read of an `i32` from the buffer
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let buffer = vec![0, 12, 0, 0, 0];
|
||||
/// assert_eq!(buffer.get_i32_volatile(1), Ok(12));
|
||||
/// ```
|
||||
@ -249,7 +272,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Perform a volatile write of an `i32` into the buffer
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut bytes = vec![0u8; 4];
|
||||
/// bytes.put_i32_ordered(0, 12);
|
||||
/// assert_eq!(bytes.get_i32_volatile(0), Ok(12));
|
||||
@ -261,7 +284,7 @@ pub trait AtomicBuffer: Deref<Target = [u8]> + DerefMut<Target = [u8]> {
|
||||
/// Write an `i32` value into the buffer without performing any synchronization
|
||||
///
|
||||
/// ```rust
|
||||
/// # use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
/// # use aeron_rs::concurrent::AtomicBuffer;
|
||||
/// let mut buffer = vec![0u8; 5];
|
||||
/// buffer.put_i32(0, 255 + 1);
|
||||
/// assert_eq!(buffer.get_i32(1), Ok(1));
|
@ -1,5 +1,5 @@
|
||||
//! Ring buffer wrapper for communicating with the Media Driver
|
||||
use crate::client::concurrent::AtomicBuffer;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
use crate::util::bit::align;
|
||||
use crate::util::{bit, AeronError, IndexT, Result};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
@ -25,6 +25,9 @@ pub mod buffer_descriptor {
|
||||
/// the start of the ring buffer metadata trailer.
|
||||
pub const CORRELATION_COUNTER_OFFSET: IndexT = (CACHE_LINE_LENGTH * 8) as IndexT;
|
||||
|
||||
/// Offset within the ring buffer trailer to the consumer heartbeat timestamp
|
||||
pub const CONSUMER_HEARTBEAT_OFFSET: IndexT = (CACHE_LINE_LENGTH * 10) as IndexT;
|
||||
|
||||
/// Total size of the ring buffer metadata trailer.
|
||||
pub const TRAILER_LENGTH: IndexT = (CACHE_LINE_LENGTH * 12) as IndexT;
|
||||
|
||||
@ -125,6 +128,7 @@ where
|
||||
head_cache_position_index: IndexT,
|
||||
head_position_index: IndexT,
|
||||
correlation_id_counter_index: IndexT,
|
||||
consumer_heartbeat_index: IndexT,
|
||||
}
|
||||
|
||||
impl<A> ManyToOneRingBuffer<A>
|
||||
@ -143,6 +147,7 @@ where
|
||||
head_cache_position_index: capacity + buffer_descriptor::HEAD_CACHE_POSITION_OFFSET,
|
||||
head_position_index: capacity + buffer_descriptor::HEAD_POSITION_OFFSET,
|
||||
correlation_id_counter_index: capacity + buffer_descriptor::CORRELATION_COUNTER_OFFSET,
|
||||
consumer_heartbeat_index: capacity + buffer_descriptor::CONSUMER_HEARTBEAT_OFFSET,
|
||||
})
|
||||
}
|
||||
|
||||
@ -377,6 +382,15 @@ where
|
||||
pub fn max_msg_length(&self) -> IndexT {
|
||||
self.max_msg_length
|
||||
}
|
||||
|
||||
/// Return the last heartbeat timestamp associated with the consumer of this queue.
|
||||
/// Timestamps are milliseconds since 1 Jan 1970, UTC.
|
||||
pub fn consumer_heartbeat_time(&self) -> i64 {
|
||||
// UNWRAP: Known-valid offset calculated during initialization
|
||||
self.buffer
|
||||
.get_i64_volatile(self.consumer_heartbeat_index)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Deref for ManyToOneRingBuffer<A>
|
||||
@ -401,8 +415,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::client::concurrent::ringbuffer::ManyToOneRingBuffer;
|
||||
use crate::client::concurrent::AtomicBuffer;
|
||||
use crate::concurrent::ringbuffer::ManyToOneRingBuffer;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
|
||||
const BUFFER_SIZE: usize = 512 + super::buffer_descriptor::TRAILER_LENGTH as usize;
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Utilities for interacting with the control protocol of the Media Driver
|
||||
//! Utilities for wrapping the command-and-control protocol with a nicer API
|
||||
use aeron_driver_sys::*;
|
||||
|
||||
/// Construct a C-compatible enum out of a set of constants.
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Bindings for the C Media Driver
|
||||
//! High-level bindings for the C Media Driver
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::path::Path;
|
||||
|
77
aeron-rs/src/driver_proxy.rs
Normal file
77
aeron-rs/src/driver_proxy.rs
Normal file
@ -0,0 +1,77 @@
|
||||
//! High level API for issuing commands to the Media Driver
|
||||
use crate::command::flyweight::Flyweight;
|
||||
use crate::command::terminate_driver::TerminateDriverDefn;
|
||||
use crate::concurrent::ringbuffer::ManyToOneRingBuffer;
|
||||
use crate::concurrent::AtomicBuffer;
|
||||
use crate::control_protocol::ClientCommand;
|
||||
use crate::util::{AeronError, IndexT, Result};
|
||||
|
||||
/// High-level interface for issuing commands to a media driver
|
||||
pub struct DriverProxy<A>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
to_driver: ManyToOneRingBuffer<A>,
|
||||
client_id: i64,
|
||||
}
|
||||
|
||||
impl<A> DriverProxy<A>
|
||||
where
|
||||
A: AtomicBuffer,
|
||||
{
|
||||
/// Initialize a new driver proxy from a command-and-control "to driver" buffer
|
||||
pub fn new(to_driver: ManyToOneRingBuffer<A>) -> Self {
|
||||
let client_id = to_driver.next_correlation_id();
|
||||
DriverProxy {
|
||||
to_driver,
|
||||
client_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the timestamp of the most recent driver heartbeat. Values are
|
||||
/// milliseconds past 1 Jan 1970, UTC.
|
||||
pub fn time_of_last_driver_keepalive(&self) -> i64 {
|
||||
self.to_driver.consumer_heartbeat_time()
|
||||
}
|
||||
|
||||
/// Get the unique identifier associated with this proxy.
|
||||
pub fn client_id(&self) -> i64 {
|
||||
self.client_id
|
||||
}
|
||||
|
||||
/// Request termination of the media driver. Optionally supply a payload on the request
|
||||
/// that will be available to the driver.
|
||||
pub fn terminate_driver(&mut self, token_buffer: Option<&[u8]>) -> Result<()> {
|
||||
let client_id = self.client_id;
|
||||
self.write_command_to_driver(|buffer: &mut [u8], length: &mut IndexT| {
|
||||
// UNWRAP: Buffer from `write_command` guaranteed to be long enough for `TerminateDriverDefn`
|
||||
let mut request = Flyweight::new::<TerminateDriverDefn>(buffer, 0).unwrap();
|
||||
|
||||
request.put_client_id(client_id).put_correlation_id(-1);
|
||||
token_buffer.map(|b| request.put_token_buffer(b));
|
||||
*length = request.length();
|
||||
|
||||
ClientCommand::TerminateDriver
|
||||
})
|
||||
}
|
||||
|
||||
fn write_command_to_driver<F>(&mut self, filler: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce(&mut [u8], &mut IndexT) -> ClientCommand,
|
||||
{
|
||||
// QUESTION: Can Rust align structs on stack?
|
||||
// C++ does some fancy shenanigans I assume help the CPU cache?
|
||||
let mut buffer = &mut [0u8; 512][..];
|
||||
let mut length = buffer.len() as IndexT;
|
||||
let msg_type_id = filler(&mut buffer, &mut length);
|
||||
|
||||
if !self
|
||||
.to_driver
|
||||
.write(msg_type_id as i32, &buffer, 0, length)?
|
||||
{
|
||||
Err(AeronError::IllegalState)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
@ -4,9 +4,13 @@
|
||||
#[cfg(target_endian = "big")]
|
||||
compile_error!("Aeron is only supported on little-endian architectures");
|
||||
|
||||
pub mod client;
|
||||
pub mod cnc_descriptor;
|
||||
pub mod command;
|
||||
pub mod concurrent;
|
||||
pub mod context;
|
||||
pub mod control_protocol;
|
||||
pub mod driver;
|
||||
pub mod driver_proxy;
|
||||
pub mod util;
|
||||
|
||||
const fn sematic_version_compose(major: u8, minor: u8, patch: u8) -> i32 {
|
||||
|
@ -1,7 +1,5 @@
|
||||
use aeron_rs::client::concurrent::broadcast::{
|
||||
buffer_descriptor, record_descriptor, BroadcastReceiver,
|
||||
};
|
||||
use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
use aeron_rs::concurrent::broadcast::{buffer_descriptor, record_descriptor, BroadcastReceiver};
|
||||
use aeron_rs::concurrent::AtomicBuffer;
|
||||
use aeron_rs::util::bit::align;
|
||||
use aeron_rs::util::IndexT;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use aeron_driver_sys::*;
|
||||
use aeron_rs::client::cnc_descriptor;
|
||||
use aeron_rs::client::cnc_descriptor::MetaDataDefinition;
|
||||
use aeron_rs::client::concurrent::ringbuffer::ManyToOneRingBuffer;
|
||||
use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
use aeron_rs::util::IndexT;
|
||||
use aeron_rs::cnc_descriptor;
|
||||
use aeron_rs::cnc_descriptor::MetaDataDefinition;
|
||||
use aeron_rs::concurrent::ringbuffer::ManyToOneRingBuffer;
|
||||
use aeron_rs::concurrent::AtomicBuffer;
|
||||
use aeron_rs::driver_proxy::DriverProxy;
|
||||
use memmap::MmapOptions;
|
||||
use std::ffi::{c_void, CString};
|
||||
use std::fs::OpenOptions;
|
||||
@ -115,24 +115,11 @@ fn cnc_terminate() {
|
||||
.to_driver_buffer_length;
|
||||
|
||||
let buffer_end = cnc_metadata_len + buffer_len as usize;
|
||||
let mut ring_buffer = ManyToOneRingBuffer::new(&mut mmap[cnc_metadata_len..buffer_end])
|
||||
let ring_buffer = ManyToOneRingBuffer::new(&mut mmap[cnc_metadata_len..buffer_end])
|
||||
.expect("Improperly sized buffer");
|
||||
|
||||
// 20 bytes: Client ID (8), correlation ID (8), token length (4)
|
||||
let mut terminate_bytes = vec![0u8; 20];
|
||||
let client_id = ring_buffer.next_correlation_id();
|
||||
terminate_bytes.put_i64_ordered(0, client_id).unwrap();
|
||||
terminate_bytes.put_i64_ordered(8, -1).unwrap();
|
||||
|
||||
let term_id: i32 = 0x0E;
|
||||
ring_buffer
|
||||
.write(
|
||||
term_id,
|
||||
&terminate_bytes,
|
||||
0,
|
||||
terminate_bytes.len() as IndexT,
|
||||
)
|
||||
.unwrap();
|
||||
let mut driver_proxy = DriverProxy::new(ring_buffer);
|
||||
driver_proxy.terminate_driver(None).unwrap();
|
||||
|
||||
// Wait for the driver to finish
|
||||
// TODO: Timeout, and then set `RUNNING` manually
|
||||
|
@ -1,8 +1,6 @@
|
||||
/// Tests based on the C++ tests included with Aeron
|
||||
use aeron_rs::client::concurrent::ringbuffer::{
|
||||
buffer_descriptor, record_descriptor, ManyToOneRingBuffer,
|
||||
};
|
||||
use aeron_rs::client::concurrent::AtomicBuffer;
|
||||
use aeron_rs::concurrent::ringbuffer::{buffer_descriptor, record_descriptor, ManyToOneRingBuffer};
|
||||
use aeron_rs::concurrent::AtomicBuffer;
|
||||
use aeron_rs::util::bit::align;
|
||||
use aeron_rs::util::IndexT;
|
||||
use std::ops::Deref;
|
||||
|
Loading…
Reference in New Issue
Block a user