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
// Copyright (C) 2017 Pietro Albini // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //! Fisher's global state. //! //! This module contains the code that keeps the Fisher global state. This is //! used for example to generate unique IDs across the codebase. The main //! [`State`](struct.State.html) struct is also marked as Sync and Send, so //! it can be used across threads without locking. use std::sync::atomic::{AtomicUsize, Ordering}; use std::cmp::PartialOrd; use std::cmp::Ordering as CmpOrdering; /// This enum represents a kind of ID. /// /// You should use this to specify which ID you do want. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum IdKind { /// This kind should be used to identify hooks. HookId, /// This kind should be used to identify threads. ThreadId, #[doc(hidden)] __NonExaustiveMatch, } /// This struct contains an unique ID. /// /// The struct is intentionally opaque, so you won't be able to get the actual /// value of the ID, but you can compare multiple IDs to get which one is /// greater, and check if multiple IDs are equal. This is done to be able to /// swap the inner implementation without breaking any code. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct UniqueId { id: usize, kind: IdKind, } impl PartialOrd for UniqueId { fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> { if self.kind == other.kind { self.id.partial_cmp(&other.id) } else { None } } } /// This struct keeps the global state of Fisher. #[derive(Debug)] pub struct State { counter: AtomicUsize, } impl State { /// Create a new instance of the struct. pub fn new() -> Self { State { counter: AtomicUsize::new(0), } } /// Get the next ID for a specific [`IdKind`](enum.IdKind.html). The ID is /// guaranteed to be unique and greater than the last ID. pub fn next_id(&self, kind: IdKind) -> UniqueId { UniqueId { id: self.counter.fetch_add(1, Ordering::SeqCst), kind: kind, } } } #[cfg(test)] mod tests { use super::{State, IdKind}; #[test] fn test_next_id() { // State must always increment let state = State::new(); let id1 = state.next_id(IdKind::HookId); let id2 = state.next_id(IdKind::HookId); let id3 = state.next_id(IdKind::ThreadId); assert!(id1 < id2); assert!(id1 == id1); assert!(id1 != id2); assert!(id1 != id3); } }