1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// License: see LICENSE file at root directory of `master` branch

//! # Blackhole
//!
//! _...to throw your threads into._
//!
//! ## Project
//!
//! - Repository: <https://bitbucket.org/haibison/blackhole-rs>
//! - License: [Free Public License 1.0.0](https://opensource.org/licenses/FPL-1.0.0)
//! - _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<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<Job>> {
//!         println!("{}", self.data);
//!         None
//!     }
//!
//! }
//!
//! let active_limit = ActiveLimit::new(4);
//! let queue_limit: usize = 10;
//! let blackhole = Blackhole::make(active_limit, queue_limit).unwrap();
//! for i in 0 .. queue_limit * 2 {
//!     match blackhole.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 is... exploded: {}", err);
//!             break;
//!         },
//!     };
//! }
//! blackhole.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  { () => { "blackhole" }}
macro_rules! version    { () => { "0.11.0" }}

/// # Crate name
pub const NAME: &'static str = "Blackhole";

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

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

/// # Crate release date (year/month/day)
pub const RELEASE_DATE: (u16, u8, u8) = (2019, 3, 15);

/// # Unique universally identifier of this crate
pub const UUID: &'static str = "f611f491-608a-44a1-8831-594a58c4c202";

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

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

#[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)),
    }
}}}