Skip to content

Commit 7ac8d15

Browse files
committed
Introduce AtomicLevelFilter
1 parent 8fc2296 commit 7ac8d15

File tree

3 files changed

+165
-20
lines changed

3 files changed

+165
-20
lines changed

spdlog/src/level.rs

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use std::{fmt, str::FromStr};
1+
use std::{
2+
fmt::{self, Debug},
3+
mem,
4+
str::FromStr,
5+
sync::atomic::Ordering,
6+
};
27

38
use crate::Error;
49

@@ -186,7 +191,7 @@ impl FromStr for Level {
186191
///
187192
/// Use [`LevelFilter::test`] method to check if a [`Level`] satisfies the
188193
/// filter condition.
189-
#[repr(align(4))]
194+
#[repr(u16, align(4))]
190195
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
191196
pub enum LevelFilter {
192197
/// Disables all levels.
@@ -269,6 +274,29 @@ impl LevelFilter {
269274
None
270275
}
271276
}
277+
278+
#[must_use]
279+
fn discriminant(&self) -> u16 {
280+
// SAFETY:
281+
// Because `LevelFilter` is marked `repr(u16)`, its layout is a `repr(C)`
282+
// `union` between `repr(C)` structs, each of which has the `u16` discriminant
283+
// as its first field, so we can read the discriminant without offsetting the
284+
// pointer.
285+
unsafe { *<*const _>::from(self).cast::<u16>() }
286+
}
287+
288+
#[must_use]
289+
fn level(&self) -> Option<Level> {
290+
match *self {
291+
Self::Equal(level)
292+
| Self::NotEqual(level)
293+
| Self::MoreSevere(level)
294+
| Self::MoreSevereEqual(level)
295+
| Self::MoreVerbose(level)
296+
| Self::MoreVerboseEqual(level) => Some(level),
297+
Self::Off | Self::All => None,
298+
}
299+
}
272300
}
273301

274302
#[cfg(feature = "log")]
@@ -281,6 +309,98 @@ impl From<log::LevelFilter> for LevelFilter {
281309
}
282310
}
283311

312+
// Atomic
313+
314+
// This struct must have the same memory layout as `LevelFilter`.
315+
#[repr(C, align(4))]
316+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
317+
struct LevelFilterLayout {
318+
discriminant: u16, // Keep the type in sync with the repr of `LevelFilter`
319+
level: Level,
320+
}
321+
322+
impl LevelFilterLayout {
323+
const UNDEFINED_FALLBACK: Level = Level::Critical;
324+
}
325+
326+
impl From<LevelFilter> for LevelFilterLayout {
327+
fn from(value: LevelFilter) -> Self {
328+
// Use `mem::transmute` here is undefined behavior, because `LevelFilter`
329+
// contains uninitialized bytes.
330+
Self {
331+
discriminant: value.discriminant(),
332+
level: value.level().unwrap_or(Self::UNDEFINED_FALLBACK),
333+
}
334+
}
335+
}
336+
337+
impl From<LevelFilterLayout> for LevelFilter {
338+
fn from(inner: LevelFilterLayout) -> Self {
339+
// SAFETY:
340+
// - Repr of `LevelFilter` is C union.
341+
// - Repr of `LevelFilterLayout` is C struct, it has the same memory layout as
342+
// `LevelFilter`.
343+
// - `LevelFilterLayout` doesn't contain uninitialized bytes.
344+
unsafe { mem::transmute::<_, LevelFilter>(inner) }
345+
}
346+
}
347+
348+
/// Atomic version of [`LevelFilter`] which can be safely shared between
349+
/// threads.
350+
pub struct AtomicLevelFilter {
351+
inner: atomic::Atomic<LevelFilterLayout>,
352+
}
353+
354+
impl AtomicLevelFilter {
355+
const ORDERING: Ordering = Ordering::Relaxed;
356+
357+
/// Creates a new `AtomicLevelFilter`.
358+
pub fn new(init: LevelFilter) -> Self {
359+
Self {
360+
inner: atomic::Atomic::new(init.into()),
361+
}
362+
}
363+
364+
/// Loads the level filter with `Relaxed` ordering.
365+
pub fn get(&self) -> LevelFilter {
366+
self.load(Self::ORDERING)
367+
}
368+
369+
/// Stores a level filter with `Relaxed` ordering.
370+
pub fn set(&self, new: LevelFilter) {
371+
self.store(new, Self::ORDERING);
372+
}
373+
374+
/// Loads the level filter.
375+
///
376+
/// # Panics
377+
///
378+
/// Panics if the ordering is `Release` or `AcqRel`.
379+
pub fn load(&self, ordering: Ordering) -> LevelFilter {
380+
self.inner.load(ordering).into()
381+
}
382+
383+
/// Stores a level filter.
384+
///
385+
/// # Panics
386+
///
387+
/// Panics if the ordering is `Acquire` or `AcqRel`.
388+
pub fn store(&self, value: LevelFilter, ordering: Ordering) {
389+
self.inner.store(value.into(), ordering);
390+
}
391+
392+
/// Stores a level filter, returning the old level filter.
393+
pub fn swap(&self, new: LevelFilter, ordering: Ordering) -> LevelFilter {
394+
self.inner.swap(new.into(), ordering).into()
395+
}
396+
}
397+
398+
impl Debug for AtomicLevelFilter {
399+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400+
self.get().fmt(f)
401+
}
402+
}
403+
284404
#[cfg(test)]
285405
mod tests {
286406
use std::mem::{align_of, size_of};
@@ -290,8 +410,10 @@ mod tests {
290410

291411
const_assert!(atomic::Atomic::<Level>::is_lock_free());
292412
const_assert!(atomic::Atomic::<LevelFilter>::is_lock_free());
413+
const_assert!(atomic::Atomic::<LevelFilterLayout>::is_lock_free());
293414
const_assert!(size_of::<Level>() * 2 == size_of::<LevelFilter>());
294-
const_assert!(align_of::<Level>() * 2 == align_of::<LevelFilter>());
415+
const_assert!(align_of::<LevelFilterLayout>() == align_of::<LevelFilter>());
416+
const_assert!(size_of::<LevelFilterLayout>() == size_of::<LevelFilter>());
295417

296418
#[test]
297419
fn from_usize() {
@@ -446,4 +568,28 @@ mod tests {
446568
LevelFilter::MoreSevereEqual(Level::Trace)
447569
);
448570
}
571+
572+
#[test]
573+
fn atomic_level_filter() {
574+
let atomic_level_filter = AtomicLevelFilter::new(LevelFilter::All);
575+
576+
let assert_this = |new: LevelFilter| {
577+
assert_ne!(atomic_level_filter.get(), new);
578+
atomic_level_filter.set(new);
579+
assert_eq!(atomic_level_filter.get(), new);
580+
};
581+
582+
fn produce_all(cond: impl Fn(Level) -> LevelFilter) -> impl Iterator<Item = LevelFilter> {
583+
Level::iter().map(cond)
584+
}
585+
586+
assert_this(LevelFilter::Off);
587+
produce_all(LevelFilter::Equal).for_each(assert_this);
588+
produce_all(LevelFilter::NotEqual).for_each(assert_this);
589+
produce_all(LevelFilter::MoreSevere).for_each(assert_this);
590+
produce_all(LevelFilter::MoreSevereEqual).for_each(assert_this);
591+
produce_all(LevelFilter::MoreVerbose).for_each(assert_this);
592+
produce_all(LevelFilter::MoreVerboseEqual).for_each(assert_this);
593+
assert_this(LevelFilter::All);
594+
}
449595
}

spdlog/src/logger.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
periodic_worker::PeriodicWorker,
77
sink::{Sink, Sinks},
88
sync::*,
9-
Level, LevelFilter, Record, Result,
9+
AtomicLevelFilter, Level, LevelFilter, Record, Result,
1010
};
1111

1212
fn check_logger_name(name: impl AsRef<str>) -> StdResult<(), SetLoggerNameError> {
@@ -108,9 +108,9 @@ fn check_logger_name(name: impl AsRef<str>) -> StdResult<(), SetLoggerNameError>
108108
/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
109109
pub struct Logger {
110110
name: Option<String>,
111-
level_filter: Atomic<LevelFilter>,
111+
level_filter: AtomicLevelFilter,
112112
sinks: Sinks,
113-
flush_level_filter: Atomic<LevelFilter>,
113+
flush_level_filter: AtomicLevelFilter,
114114
error_handler: RwLock<ErrorHandler>,
115115
periodic_flusher: Mutex<Option<(Duration, PeriodicWorker)>>,
116116
}
@@ -258,7 +258,7 @@ impl Logger {
258258
/// Gets the flush level filter.
259259
#[must_use]
260260
pub fn flush_level_filter(&self) -> LevelFilter {
261-
self.flush_level_filter.load(Ordering::Relaxed)
261+
self.flush_level_filter.get()
262262
}
263263

264264
/// Sets a flush level filter.
@@ -286,14 +286,13 @@ impl Logger {
286286
/// trace!(logger: logger, "world"); // Logs and flushes the buffer once
287287
/// ```
288288
pub fn set_flush_level_filter(&self, level_filter: LevelFilter) {
289-
self.flush_level_filter
290-
.store(level_filter, Ordering::Relaxed);
289+
self.flush_level_filter.set(level_filter);
291290
}
292291

293292
/// Gets the log level filter.
294293
#[must_use]
295294
pub fn level_filter(&self) -> LevelFilter {
296-
self.level_filter.load(Ordering::Relaxed)
295+
self.level_filter.get()
297296
}
298297

299298
/// Sets the log level filter.
@@ -302,7 +301,7 @@ impl Logger {
302301
///
303302
/// See [`Logger::should_log`].
304303
pub fn set_level_filter(&self, level_filter: LevelFilter) {
305-
self.level_filter.store(level_filter, Ordering::Relaxed);
304+
self.level_filter.set(level_filter);
306305
}
307306

308307
/// Sets automatic periodic flushing.
@@ -473,9 +472,9 @@ impl Logger {
473472
fn clone_lossy(&self) -> Self {
474473
Logger {
475474
name: self.name.clone(),
476-
level_filter: Atomic::new(self.level_filter()),
475+
level_filter: AtomicLevelFilter::new(self.level_filter()),
477476
sinks: self.sinks.clone(),
478-
flush_level_filter: Atomic::new(self.flush_level_filter()),
477+
flush_level_filter: AtomicLevelFilter::new(self.flush_level_filter()),
479478
periodic_flusher: Mutex::new(None),
480479
error_handler: RwLock::new(self.error_handler.read_expect().clone()),
481480
}
@@ -671,9 +670,9 @@ impl LoggerBuilder {
671670

672671
let logger = Logger {
673672
name: self.name.clone(),
674-
level_filter: Atomic::new(self.level_filter),
673+
level_filter: AtomicLevelFilter::new(self.level_filter),
675674
sinks: self.sinks.clone(),
676-
flush_level_filter: Atomic::new(self.flush_level_filter),
675+
flush_level_filter: AtomicLevelFilter::new(self.flush_level_filter),
677676
error_handler: RwLock::new(self.error_handler.clone()),
678677
periodic_flusher: Mutex::new(None),
679678
};

spdlog/src/sink/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub use write_sink::*;
6464
use crate::{
6565
formatter::{Formatter, FullFormatter},
6666
sync::*,
67-
Error, ErrorHandler, Level, LevelFilter, Record, Result,
67+
AtomicLevelFilter, Error, ErrorHandler, Level, LevelFilter, Record, Result,
6868
};
6969

7070
/// Contains definitions of sink properties.
@@ -78,7 +78,7 @@ use crate::{
7878
/// types, changing behavior), this struct is not needed. Instead, define
7979
/// properties manually within your sink, and then implement [`SinkPropAccess`].
8080
pub struct SinkProp {
81-
level_filter: Atomic<LevelFilter>,
81+
level_filter: AtomicLevelFilter,
8282
formatter: RwLockMappable<Box<dyn Formatter>>,
8383
error_handler: RwLock<ErrorHandler>,
8484
}
@@ -93,7 +93,7 @@ impl Default for SinkProp {
9393
/// | `error_handler` | [`ErrorHandler::default()`] |
9494
fn default() -> Self {
9595
Self {
96-
level_filter: Atomic::new(LevelFilter::All),
96+
level_filter: AtomicLevelFilter::new(LevelFilter::All),
9797
formatter: RwLockMappable::new(Box::new(FullFormatter::new())),
9898
error_handler: RwLock::new(ErrorHandler::default()),
9999
}
@@ -104,12 +104,12 @@ impl SinkProp {
104104
/// Gets the log level filter.
105105
#[must_use]
106106
pub fn level_filter(&self) -> LevelFilter {
107-
self.level_filter.load(Ordering::Relaxed)
107+
self.level_filter.get()
108108
}
109109

110110
/// Sets the log level filter.
111111
pub fn set_level_filter(&self, level_filter: LevelFilter) {
112-
self.level_filter.store(level_filter, Ordering::Relaxed)
112+
self.level_filter.set(level_filter)
113113
}
114114

115115
/// Gets the formatter.

0 commit comments

Comments
 (0)