#id-generator #id #unique-id #per-thread #thread #unique #range

no-std tlid

Thread Local ID generator by predefined range without atomics/locks/random/time

4 releases

0.2.2 Feb 5, 2020
0.2.1 Feb 3, 2020
0.2.0 Jan 28, 2020
0.1.0 Jan 26, 2020

#396 in Concurrency

Apache-2.0 OR MIT

25KB
484 lines

pipeline status coverage report lines of code

TLID

low level generation of unique ids by assigning a range per thread.

Pros:

  • unique number, simple incremented
  • no locks, no atomic for ::next()
  • all dependencies are optional
  • speed: 600,000,000/s
  • example coding
  • strict ci tests

Cons:

  • range needs to be know before, e.g. u64, with 1024 threads only leaves 2^54 usable ids per thread
  • not cryptographic secure (simple inc)
  • creation and deletion of pools requieres syncronisation
  • beta: before 0.9 tlid isn't production ready, expect bugs or non-optimal ID return behavior

There are alot of Id crates but almost all use either some random values, timing, or atomic behavior to be unique over multiple threads. Tlid (thread local unique id) aims to archive uniqueness by assigning a initial range to every thread. This requieres setting a range for each pool (and subpool), which makes ::next() super fast.

There are 3 options behaviors for ::next(), which cannot not be mixed

  • Checked: gurantees a unique id at the cost of a range check with each operation
  • Wrapping: has the same check, but instead of rendering the Pool useless after all IDs are used, it wraps around. IDs are not unique anymore but this is useful for short buffers like for networking
  • Unchecked: fastes method with no guarantee, bounds are not checked in ::next() method.

Dependencies

[dependencies]
tlid = "0.2"

Examples

// see examples folder

use tlid::{Pool, Checked};
use std::{
    thread,
    time::Duration,
    result::Result,
    error::Error,
};

fn worker(thread: u8, p: &mut Pool<Checked<u64>>) {
    //work
    for _ in 0..2 {
        thread::sleep(Duration::from_millis(1));
        println!("[{}] did work: {}", thread, p.next().unwrap());
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut p = Pool::new_full();
    for i in 0..5 {
        let mut local_pool = p.subpool(5)?;
        thread::spawn(move ||
            worker(i, &mut local_pool)
        );
    }
    thread::sleep(Duration::from_millis(100));
    Ok(())
}

Example output (as you see, all ids are unique):

[0] did work: 0
[1] did work: 2
[2] did work: 4
[0] did work: 1
[1] did work: 3
[3] did work: 6
[4] did work: 8
[2] did work: 5
[3] did work: 7
[4] did work: 9

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~98–335KB