Skip to content

Commit

Permalink
memfaultd 1.14.0 (Build 2370319)
Browse files Browse the repository at this point in the history
  • Loading branch information
Memfault Inc. committed Aug 22, 2024
1 parent 11adc90 commit 3a9e0c5
Show file tree
Hide file tree
Showing 93 changed files with 2,911 additions and 1,088 deletions.
399 changes: 375 additions & 24 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

members = [
"memfaultd",
"memfaultc-sys"
"memfaultc-sys",
"memfault-ssf"
]
resolver = "2"

Expand Down
6 changes: 3 additions & 3 deletions VERSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
BUILD ID: 2255289
GIT COMMIT: add51b077b
VERSION: 1.13.0
BUILD ID: 2370319
GIT COMMIT: 41269b2621
VERSION: 1.14.0
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
allow-unwrap-in-tests = true
12 changes: 12 additions & 0 deletions memfault-ssf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "memfault-ssf"
version = "1.14.0"
edition = "2021"
description = "Supporting crate for the Memfault memfaultd embedded Linux agent"
homepage = "https://github.com/memfault/memfaultd"
documentation = "https://docs.memfault.com/"
license-file = "License.txt"
readme = "README.md"
repository = "https://github.com/memfault/memfaultd"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
31 changes: 31 additions & 0 deletions memfault-ssf/License.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Copyright (c) 2019 - Present, Memfault
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code or in binary form must reproduce
the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution.

2. Neither the name of Memfault nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

3. This software, with or without modification, must only be used with
the Memfault services and integrated with the Memfault server.

4. Any software provided in binary form under this license must not be
reverse engineered, decompiled, modified and/or disassembled.

THIS SOFTWARE IS PROVIDED BY MEMFAULT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL MEMFAULT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
5 changes: 5 additions & 0 deletions memfault-ssf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `memfault-ssf`

Internal crate for use by the
[`memfaultd`](https://github.com/memfault/memfaultd/) crate, not for general
use.
100 changes: 100 additions & 0 deletions memfault-ssf/src/envelope.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// Copyright (c) Memfault, Inc.
// See License.txt for details
//! Provide a `struct Envelope<S>` that can be used to wrap messages of any type
//! M, as long as:
//! - S is a service
//! - S can handle the type M.
//!
//! Because the type `Envelope<S>` is only generic on the service, it enables
//! grouping together multiple messages of different types.
//!
//! This is the magic that makes it possible to deliver messages of multiple
//! unrelated types (they are not one enum) to services.
//!
//! The implementation relies on dynamic dispatch to an internal hidden type
//! that supports calling `envelope->handle(service)` (an inversion of
//! responsibility).
use std::{
any::TypeId,
sync::mpsc::{channel, Receiver, Sender},
time::Instant,
};

use crate::{DeliveryStats, Handler, Message, Service};

/// Wrap a message that can be handled by `S`.
pub struct Envelope<S> {
message: Box<dyn EnvelopeT<S>>,
}

impl<S: Service> Envelope<S> {
pub fn wrap<M>(message: M) -> Self
where
M: Message,
S: Handler<M>,
{
Self::wrap_with_reply(message).0
}

pub fn wrap_with_reply<M>(message: M) -> (Self, Receiver<M::Reply>)
where
M: Message,
S: Handler<M>,
{
let (ack_sender, ack_receiver) = channel();
(
Envelope {
message: Box::new(EnvelopeTImpl {
timestamp: Instant::now(),
message: Some(message),
ack_sender,
}),
},
ack_receiver,
)
}

pub fn deliver_to(&mut self, service: &mut S) -> Result<DeliveryStats, &str> {
self.message.handle(service)
}

pub fn message_type_id(&self) -> Option<TypeId> {
self.message.type_id()
}
}

trait EnvelopeT<S: Service>: Send {
fn type_id(&self) -> Option<TypeId>;
fn handle(&mut self, service: &mut S) -> Result<DeliveryStats, &str>;
}
struct EnvelopeTImpl<M>
where
M: Message,
{
message: Option<M>,
ack_sender: Sender<M::Reply>,
timestamp: Instant,
}
impl<S: Service + Handler<M>, M: Message> EnvelopeT<S> for EnvelopeTImpl<M> {
fn type_id(&self) -> Option<TypeId> {
self.message.as_ref().map(|m| m.type_id())
}

fn handle(&mut self, service: &mut S) -> Result<DeliveryStats, &'static str> {
if let Some(message) = self.message.take() {
let processing_at = Instant::now();
let r = service.deliver(message);

let queued = processing_at - self.timestamp;
let processing = Instant::now() - processing_at;

// We ignore errors to deliver the ack as the caller might have moved on with their life.
let _error = self.ack_sender.send(r);
Ok(DeliveryStats { queued, processing })
} else {
Err("Attempt to deliver multiple times")
}
}
}
95 changes: 95 additions & 0 deletions memfault-ssf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// Copyright (c) Memfault, Inc.
// See License.txt for details
//! Simple Service Framework
//!
//! This library provides a simple abstraction to implement a service oriented
//! architecture in a non-async Rust program.
//!
//! Each service runs in its own thread with a processing loop waiting on a
//! channel.
//!
//! This library provides a few essential traits:
//!
//! - `Service`: a trait implemented by services
//! - `Message`: a trait implemented by messages
//! - `Handler<M: Message>`: a trait implemented by services that can handle
//! messages of type `M`.
//!
//! We provide some important structs to deploy the services:
//! - `ServiceThread`: will start and run a service inside a dedicated thread.
//! It returns a `Mailbox`.
//! - `Mailbox<S: Service>`: a lightweight (cheap to `clone()`) handle to send
//! messages to a thread.
//! - `Scheduler`: a utility thread which keeps a schedule of messages that need
//! to be sent at fixed intervals.
//!
//! As well as important testing utilities that are a big part of the value
//! provided by this framework:
//! - `ServiceJig`: a way to run a service inside a test without using threads.
//! The test can precisely decide when messages should be delivered and inspect
//! the state of the service at any time.
//! - `ServiceMock`: a service mock. Use this when you just need a place where
//! to send messages. Your test can then verify that the right messages were
//! sent to the mock.
//!
use std::any::Any;

mod envelope;
mod mailbox;
mod msg_mailbox;
mod scheduler;
mod service_jig;
mod service_mock;
mod service_thread;
mod shared_service_thread;
mod stats;
mod system_messages;

pub use envelope::*;
pub use mailbox::*;
pub use msg_mailbox::*;
pub use scheduler::*;
pub use service_jig::*;
pub use service_mock::*;
pub use service_thread::*;
pub use shared_service_thread::*;
pub use stats::*;
pub use system_messages::*;

/// All services should implement this trait. It guarantees that we will be able
/// to run the service inside a thread (it is `Send`).
pub trait Service: Send {
fn name(&self) -> &str;
}

/// Any type that will be sent between services needs to implement this trait.
///
/// Sending a Reply is optional (use `type Reply = ()` if you will not send a reply).
pub trait Message: Send + Sync + Any + 'static {
type Reply: Send;
}

/// Implement this trait to indicate that your service can process a specific message.
///
/// The `deliver()` method is passed a `&mut self` reference to the service,
/// making it very easy to update your state. You can optionally include a
/// reply.
pub trait Handler<M: Message> {
fn deliver(&mut self, m: M) -> M::Reply;
}

/// Blanket implementation of Message for any Vec<M>. You lose the return value.
impl<M: Message> Message for Vec<M> {
type Reply = ();
}

/// Blanket implementation of delivering a `Vec<Message>` to a `Handler<Message>`.
impl<M: Message, S: Handler<M>> Handler<Vec<M>> for S {
fn deliver(&mut self, messages: Vec<M>) {
for m in messages {
self.deliver(m);
}
}
}
69 changes: 69 additions & 0 deletions memfault-ssf/src/mailbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright (c) Memfault, Inc.
// See License.txt for details
use std::{
error::Error,
fmt::Display,
sync::mpsc::{channel, Receiver, Sender},
};

use crate::{Envelope, Handler, Message, Service};

/// The only reason for a message to fail to send is if the receiver channel is closed.
// An improvement would be to return the message back to the sender (the
// channel does it but after we wrap it in an envelope, it's complicated...)
#[derive(Debug)]
pub enum MailboxError {
SendChannelClosed,
NoResponse,
}

impl Display for MailboxError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "error sending message")
}
}
impl Error for MailboxError {}

pub struct Mailbox<S: Service> {
sender: Sender<Envelope<S>>,
}

impl<S: Service> Mailbox<S> {
pub fn create() -> (Self, Receiver<Envelope<S>>) {
let (sender, receiver) = channel();
(Mailbox { sender }, receiver)
}

pub fn send_and_forget<M>(&self, message: M) -> Result<(), MailboxError>
where
M: Message,
S: Handler<M>,
{
self.sender
.send(Envelope::wrap(message))
.map_err(|_e| MailboxError::SendChannelClosed)
}

pub fn send_and_wait_for_reply<M>(&self, message: M) -> Result<M::Reply, MailboxError>
where
M: Message,
S: Handler<M>,
{
let (envelope, ack_receiver) = Envelope::wrap_with_reply(message);

self.sender
.send(envelope)
.map_err(|_e| MailboxError::SendChannelClosed)?;

ack_receiver.recv().map_err(|_e| MailboxError::NoResponse)
}
}

impl<S: Service> Clone for Mailbox<S> {
fn clone(&self) -> Self {
Mailbox {
sender: self.sender.clone(),
}
}
}
Loading

0 comments on commit 3a9e0c5

Please sign in to comment.