Skip to content

Commit

Permalink
bump soprintln
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Jul 18, 2024
1 parent d55cdd1 commit 9679ef2
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 190 deletions.
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ check:
cargo hack --each-feature --exclude-all-features clippy --manifest-path rubicon/Cargo.toml

test:
cargo run --manifest-path test-crates/bin/Cargo.toml
SOPRINTLN=1 cargo run --manifest-path test-crates/bin/Cargo.toml
1 change: 0 additions & 1 deletion rubicon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ paste = { version = "1.0.15", optional = true }
default = []
export-globals = ["dep:paste"]
import-globals = ["dep:paste"]
soprintln = []
194 changes: 20 additions & 174 deletions rubicon/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[cfg(all(feature = "export-globals", feature = "import-globals"))]
compile_error!("The features `export-globals` and `import-globals` cannot be used together");
compile_error!("The features `export-globals` and `import-globals` are mutually exclusive, see https://github.com/bearcove/rubicon");

#[cfg(any(feature = "export-globals", feature = "import-globals"))]
pub use paste::paste;
Expand Down Expand Up @@ -48,17 +48,24 @@ impl<T> Deref for TrustedExternDouble<T> {
/// Usage:
///
/// ```ignore
/// use rubicon::process_local;
///
/// process_local! {
/// static FOO: u32 = 42;
/// rubicon::thread_local! {
/// static FOO: AtomicU32 = AtomicU32::new(42);
/// }
/// ```
///
/// This will import `FOO` if the `import-globals` feature is enabled, and export it if the
/// `export-globals` feature is enabled.
///
/// If neither feature is enabled, this will expand to the static declaration itself.
/// If neither feature is enabled, this will be equivalent to `std::thread_local!`.
///
/// This macro supports multiple declarations:
///
/// ```ignore
/// rubicon::thread_local! {
/// static FOO: AtomicU32 = AtomicU32::new(42);
/// static BAR: AtomicU32 = AtomicU32::new(43);
/// }
/// ```
#[cfg(not(any(feature = "import-globals", feature = "export-globals")))]
#[macro_export]
macro_rules! thread_local {
Expand Down Expand Up @@ -148,6 +155,13 @@ macro_rules! thread_local_inner {
///
/// This macro supports multiple declarations, along with `static mut` declarations
/// (which have a slightly different expansion).
///
/// ```ignore
/// rubicon::thread_local! {
/// static FOO: AtomicU32 = AtomicU32::new(42);
/// static mut BAR: Dispatcher = Dispatcher::new();
/// }
/// ```
#[cfg(all(not(feature = "import-globals"), not(feature = "export-globals")))]
#[macro_export]
macro_rules! process_local {
Expand Down Expand Up @@ -242,171 +256,3 @@ macro_rules! process_local_inner_mut {
}
};
}

//==============================================================================
// soprintln
//==============================================================================

/// Note: there's one copy of this static per shared object on purpose — that's the one
/// static we DON'T want to deduplicate.
#[used]
static SHARED_OBJECT_ID_REF: u64 = 0;

/// Returns a unique identifier for the current shared object.
pub fn shared_object_id() -> u64 {
&SHARED_OBJECT_ID_REF as *const _ as u64
}

/// Defined to `I` when importing globals, `E` when exporting globals, and `N` otherwise.
#[cfg(feature = "import-globals")]
pub static RUBICON_MODE: &str = "I"; // "import"

/// Defined to `I` when importing globals, `E` when exporting globals, and `N` otherwise.
#[cfg(feature = "export-globals")]
pub static RUBICON_MODE: &str = "E"; // "export"

/// Defined to `I` when importing globals, `E` when exporting globals, and `N` otherwise.
#[cfg(not(any(feature = "import-globals", feature = "export-globals")))]
pub static RUBICON_MODE: &str = "N"; // "normal"

#[cfg(all(feature = "import-globals", feature = "export-globals"))]
compile_error!("The features \"import-globals\" and \"export-globals\" are mutually exclusive");

/// A `u64` whose 24-bit ANSI color is determined by its value.
///
/// Used by the [`soprintln`] macro to visually distinguish shared objects and threads.
pub struct Beacon<'a> {
fg: (u8, u8, u8),
bg: (u8, u8, u8),
name: &'a str,
val: u64,
}

impl<'a> Beacon<'a> {
/// Creates a new `Beacon` from a pointer.
pub fn from_ptr<T>(name: &'a str, ptr: *const T) -> Self {
Self::new(name, ptr as u64)
}

/// Creates a new `Beacon` from a reference.
pub fn from_ref<T>(name: &'a str, r: &T) -> Self {
Self::new(name, r as *const T as u64)
}

/// Creates a new `Beacon` with the given extra string and value.
pub fn new(name: &'a str, u: u64) -> Self {
fn hash(mut x: u64) -> u64 {
const K: u64 = 0x517cc1b727220a95;
x = x.wrapping_mul(K);
x ^= x >> 32;
x = x.wrapping_mul(K);
x ^= x >> 32;
x = x.wrapping_mul(K);
x
}

let hashed_float = (hash(u) as f64) / (u64::MAX as f64);
let h = hashed_float * 360.0;
let s = 50.0;
let l = 70.0;

fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (u8, u8, u8) {
let h = h / 360.0;
let s = s / 100.0;
let l = l / 100.0;

let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
let m = l - c / 2.0;

let (r, g, b) = match (h * 6.0) as u8 {
0 | 6 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x),
};

(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}

let fg = hsl_to_rgb(h, s, l);
let bg = hsl_to_rgb(h, s * 0.8, l * 0.5);

Self {
fg,
bg,
name,
val: u,
}
}
}

impl<'a> std::fmt::Display for Beacon<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"\x1b[48;2;{};{};{}m\x1b[38;2;{};{};{}m{}#{:0x}\x1b[0m",
self.bg.0, self.bg.1, self.bg.2, self.fg.0, self.fg.1, self.fg.2, self.name, self.val
)
}
}

/// Prints a message, prefixed with a cycling millisecond timestamp (wraps at 99999),
/// a colorized shared object id, a colorized thread name+id, and the given message.
#[macro_export]
#[cfg(feature = "soprintln")]
macro_rules! soprintln {
($($arg:tt)*) => {
{
use std::sync::atomic::{AtomicBool, Ordering};
static ENV_CHECKED: std::sync::Once = std::sync::Once::new();
static SHOULD_PRINT: AtomicBool = AtomicBool::new(false);
ENV_CHECKED.call_once(|| {
let should_print = std::env::var("SO_PRINTLN").map(|v| v == "1").unwrap_or(false);
SHOULD_PRINT.store(should_print, Ordering::Relaxed);
});

if SHOULD_PRINT.load(Ordering::Relaxed) {
// this formatting is terribly wasteful — PRs welcome

let so_id = $crate::shared_object_id();
let so_mode_and_id = $crate::Beacon::new($crate::RUBICON_MODE, so_id);
let curr_thread = std::thread::current();
let tid = format!("{:?}", curr_thread.id());
// strip `ThreadId(` prefix
let tid = tid.strip_prefix("ThreadId(").unwrap_or(&tid);
// strip `)` suffix
let tid = tid.strip_suffix(")").unwrap_or(&tid);
// parse tid as u64
let tid = tid.parse::<u64>().unwrap_or(0);

let thread_name = curr_thread.name().unwrap_or("<unnamed>");
let thread = $crate::Beacon::new(thread_name, tid);

let timestamp = ::std::time::SystemTime::now().duration_since(::std::time::UNIX_EPOCH).unwrap().as_millis() % 99999;
// FIXME: this is probably not necessary, but without it, rustc complains about
// capturing variables in format_args?
let msg = format!($($arg)*);
eprintln!("{timestamp:05} {so_mode_and_id} {thread} {msg}");
}
}
};
}

/// `soprintln!` prints a message prefixed by a truncated timestamp, shared object ID and thread ID.
///
/// It is costly, which is why it's behind a cargo feature AND an environment variable.
///
/// To see soprintln output, enable the `soprintln` cargo feature, and set the `SO_PRINTLN`
/// environment variable to `1`.
#[macro_export]
#[cfg(not(feature = "soprintln"))]
macro_rules! soprintln {
($($arg:tt)*) => {};
}
1 change: 1 addition & 0 deletions rubicon/src/soprintln.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

7 changes: 7 additions & 0 deletions test-crates/bin/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions test-crates/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ edition = "2021"
[dependencies]
exports = { version = "0.1.0", path = "../exports" }
libloading = "0.8.4"
rubicon = { version = "2.0.0", path = "../../rubicon", features = [
"soprintln",
] }
rubicon = { version = "2.0.0", path = "../../rubicon" }
soprintln = { version = "3.0.0", features = ["print"] }
5 changes: 2 additions & 3 deletions test-crates/bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::sync::atomic::Ordering;

use exports::{self as _, mokio};
use rubicon::soprintln;
use soprintln::soprintln;

fn main() {
std::env::set_var("SO_PRINTLN", "1");

soprintln::init!();
let exe_path = std::env::current_exe().expect("Failed to get current exe path");
let project_root = exe_path
.parent()
Expand Down
7 changes: 7 additions & 0 deletions test-crates/mod_a/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions test-crates/mod_a/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ crate-type = ["cdylib"]

[dependencies]
mokio = { version = "0.1.0", path = "../mokio", features = ["import-globals"] }
rubicon = { version = "2.0.0", path = "../../rubicon", features = [
"soprintln",
] }
rubicon = { version = "2.0.0", path = "../../rubicon" }
soprintln = { version = "3.0.0", features = ["print"] }
3 changes: 2 additions & 1 deletion test-crates/mod_a/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use rubicon::soprintln;
use soprintln::soprintln;
use std::sync::atomic::Ordering;

#[no_mangle]
pub fn init() {
soprintln::init!();
mokio::MOKIO_TL1.with(|s| s.fetch_add(1, Ordering::Relaxed));
mokio::MOKIO_PL1.fetch_add(1, Ordering::Relaxed);
soprintln!("DANGEROUS is now {}", unsafe {
Expand Down
7 changes: 7 additions & 0 deletions test-crates/mod_b/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions test-crates/mod_b/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ crate-type = ["cdylib"]

[dependencies]
mokio = { version = "0.1.0", path = "../mokio", features = ["import-globals"] }
rubicon = { version = "2.0.0", path = "../../rubicon", features = [
"soprintln",
] }
rubicon = { version = "2.0.0", path = "../../rubicon" }
soprintln = { version = "3.0.0", features = ["print"] }
3 changes: 2 additions & 1 deletion test-crates/mod_b/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use rubicon::soprintln;
use soprintln::soprintln;
use std::sync::atomic::Ordering;

#[no_mangle]
pub fn init() {
soprintln::init!();
mokio::MOKIO_TL1.with(|s| s.fetch_add(1, Ordering::Relaxed));
mokio::MOKIO_PL1.fetch_add(1, Ordering::Relaxed);
soprintln!("DANGEROUS is now {}", unsafe {
Expand Down

0 comments on commit 9679ef2

Please sign in to comment.