// License: see LICENSE file at root directory of `master` branch

//! # Black Hole
//!
//! _...to throw your threads into._
//!
//! ## Project
//!
//! - Repository: <https://bitbucket.org/haibison/blackhole>
//! - License: Nice License 1.0.0 _(see LICENSE file at root directory of `master` branch)_
//! - _This project follows [Semantic Versioning 2.0.0]_
//!
//! ## Features
//!
//! Simple thread manager.
//!
//! ## Design
//!
//! It uses a [channel][std::sync::mpsc] and one job manager thread:
//!
//! - When you throw a new job, it checks for active job count.
//!
//!     + If that count is smaller than limit, it spawns a new runner thread to run that job.
//!     + If that count is equal to limit:
//!
//!         * If job queue is smaller than limit, it pushes the job into the queue.
//!         * Otherwise the job is discarded. An error message is printed to stderr. And the job is returned to you for recovery.
//!
//! - When the runner thread finishes the first job, it contacts job manager to ask for new jobs to run. If there are no new jobs, it finishes
//!   itself and notifies job manager about that.
//! - When job manager receives job-finished-message, it checks job queue to spawn new runner threads, if necessary.
//!
//! ## Examples
//!
//! ```
//! use blackhole::{ActiveLimit, BlackHole, Job};
//!
//! /// # This job takes a `usize`, if it's odd, sends it to Printer
//! struct Handler {
//!     data: usize,
//! }
//!
//! impl Job for Handler {
//!
//!     fn run(&mut self) -> Option<Box<dyn Job>> {
//!         match self.data % 2 {
//!             0 => None,
//!             _ => Some(Box::new(Printer { data: self.data })),
//!         }
//!     }
//!
//! }
//!
//! /// # This job prints data and does nothing else
//! struct Printer {
//!     data: usize,
//! }
//!
//! impl Job for Printer {
//!
//!     fn run(&mut self) -> Option<Box<dyn Job>> {
//!         println!("{}", self.data);
//!         None
//!     }
//!
//! }
//!
//! let active_limit: ActiveLimit = 4;
//! let queue_limit: usize = 10;
//! let black_hole = BlackHole::make(active_limit, queue_limit).unwrap();
//! for i in 0 .. queue_limit * 2 {
//!     match black_hole.throw(Handler { data: i }) {
//!         Ok(None) => println!("Job is accepted"),
//!         // If job is discarded, it will be sent back here.
//!         Ok(Some(job)) => {
//!             eprintln!("Job is discarded! We'll run it ourselves!");
//!             blackhole::run_to_end(job);
//!         },
//!         Err(err) => {
//!             eprintln!("BlackHole... exploded: {}", err);
//!             break;
//!         },
//!     };
//! }
//! black_hole.escape_on_idle().unwrap();
//! ```
//!
//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
//! [std::sync::mpsc]: https://doc.rust-lang.org/std/sync/mpsc/index.html

#![warn(missing_docs)]

#[macro_use]
#[allow(unused_macros)]
mod __;
mod job;
mod root;

pub use job::*;
pub use root::*;

pub mod version_info;

// ╔═════════════════╗
// ║   IDENTIFIERS   ║
// ╚═════════════════╝

macro_rules! code_name  { () => { "black-hole" }}
macro_rules! version    { () => { "0.18.0" }}

/// # Crate name
pub const NAME: &str = "Black Hole";

/// # Crate code name
pub const CODE_NAME: &str = code_name!();

/// # ID of this crate
pub const ID: &str = concat!(
    "e64a08f1-fdd25485-2acc4e42-e735d450-9f0f609c-7ffb34ec-fed9b97f-bf66bc0d-",
    "447179f2-7c5d9ff7-69c111e2-a0a2769d-071a4c1d-4a9e6ac0-c37fec16-19aa9e5e",
);

/// # Crate version
pub const VERSION: &str = version!();

/// # Crate release date (year/month/day)
pub const RELEASE_DATE: (u16, u8, u8) = (2020, 12, 13);

/// # Tag, which can be used for logging...
pub const TAG: &str = concat!(code_name!(), "::e64a08f1::", version!());

// ╔════════════════════╗
// ║   IMPLEMENTATION   ║
// ╚════════════════════╝

/// # Result type used in this crate
pub type Result<T> = core::result::Result<T, std::io::Error>;

#[test]
fn test_crate_version() {
    assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
}

/// # Gets online processors
///
/// This ***unsafe-unstable*** macro requires `libc` crate. `$fake` is an `Option<Into<u64>>`, which will be used if calling to `libc` fails. If
/// you provide `None`, `1_u8` will be used.
///
/// ## Examples
///
/// ```
/// let online_processors: u64 = unsafe {
///     blackhole::get_online_processors!(None)
/// };
/// assert!(online_processors > 0);
/// ```
///
/// ## References
///
/// - <https://www.gnu.org/software/libc/manual/html_node/Processor-Resources.html>
#[macro_export]
macro_rules! get_online_processors { ($fake: expr) => {{
    let tmp: i64 = libc::sysconf(libc::_SC_NPROCESSORS_ONLN);
    match tmp > 0 {
        true => tmp as u64,
        false => u64::from($fake.unwrap_or(1_u8)),
    }
}}}
