// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! Registering events streams.
//!
//! This code holds the logic that is used for the network service to inform other parts of
//! Bizinikiwi about what is happening.
//!
//! # Usage
//!
//! - Create an instance of [`OutChannels`].
//! - Create channels using the [`channel`] function. The receiving side implements the `Stream`
//! trait.
//! - You cannot directly send an event on a sender. Instead, you have to call
//! [`OutChannels::push`] to put the sender within a [`OutChannels`].
//! - Send events by calling [`OutChannels::send`]. Events are cloned for each sender in the
//! collection.
use crate::event::Event;
use futures::{prelude::*, ready, stream::FusedStream};
use log::{debug, error};
use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64};
use std::{
backtrace::Backtrace,
cell::RefCell,
fmt,
pin::Pin,
task::{Context, Poll},
};
/// Log target for this file.
pub const LOG_TARGET: &str = "sub-libp2p::out_events";
/// Creates a new channel that can be associated to a [`OutChannels`].
///
/// The name is used in Prometheus reports, the queue size threshold is used
/// to warn if there are too many unprocessed events in the channel.
pub fn channel(name: &'static str, queue_size_warning: usize) -> (Sender, Receiver) {
let (tx, rx) = async_channel::unbounded();
let tx = Sender {
inner: tx,
name,
queue_size_warning,
warning_fired: SenderWarningState::NotFired,
creation_backtrace: Backtrace::force_capture(),
metrics: None,
};
let rx = Receiver { inner: rx, name, metrics: None };
(tx, rx)
}
/// A state of a sender warning that is used to avoid spamming the logs.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SenderWarningState {
/// The warning has not been fired yet.
NotFired,
/// The warning has been fired, and the channel is full
FiredFull,
/// The warning has been fired and the channel is not full anymore.
FiredFree,
}
/// Sending side of a channel.
///
/// Must be associated with an [`OutChannels`] before anything can be sent on it
///
/// > **Note**: Contrary to regular channels, this `Sender` is purposefully designed to not
/// implement the `Clone` trait e.g. in Order to not complicate the logic keeping the metrics in
/// sync on drop. If someone adds a `#[derive(Clone)]` below, it is **wrong**.
pub struct Sender {
inner: async_channel::Sender,
/// Name to identify the channel (e.g., in Prometheus and logs).
name: &'static str,
/// Threshold queue size to generate an error message in the logs.
queue_size_warning: usize,
/// We generate the error message only once to not spam the logs after the first error.
/// Subsequently we indicate channel fullness on debug level.
warning_fired: SenderWarningState,
/// Backtrace of a place where the channel was created.
creation_backtrace: Backtrace,
/// Clone of [`Receiver::metrics`]. Will be initialized when [`Sender`] is added to
/// [`OutChannels`] with `OutChannels::push()`.
metrics: Option,
}
impl fmt::Debug for Sender {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Sender").finish()
}
}
impl Drop for Sender {
fn drop(&mut self) {
if let Some(metrics) = self.metrics.as_ref() {
metrics.num_channels.with_label_values(&[self.name]).dec();
}
}
}
/// Receiving side of a channel.
pub struct Receiver {
inner: async_channel::Receiver,
name: &'static str,
/// Initially contains `None`, and will be set to a value once the corresponding [`Sender`]
/// is assigned to an instance of [`OutChannels`].
metrics: Option,
}
impl Stream for Receiver {
type Item = Event;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll