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
extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::sync::mpsc::{channel, Sender};

use misc::{NamedStorage, SerializationFormat, Subset};
use task::{Op, PlainRawStorage, KeyedRawStorage, TelemetryTask};
use indexing::*;

///
/// The Telemetry service.
///
/// The service is in charge of maintaining the data recorded by the
/// histograms. Each application using telemetry needs one instance of
/// the service (or more, but this should seldom be necessary). The
/// data is stored and processed in a dedicated background thread and
/// the memory is recollected only when the service is dropped.
///
/// # Panics
///
/// The service will panic if an attempt is made to register two
/// histograms with the same key.
///
impl Service {
    ///
    /// Create a new instance of the service.
    ///
    /// This immediately launches the thread owning the data.
    ///
    /// If `is_active` is `false`, the service will immediately accept
    /// records. Otherwise, the service will only start recording once
    /// `set_active(true)` has been called.
    ///
    pub fn new(is_active: bool) -> Service {
        let (sender, receiver) = channel();
        thread::spawn(|| {
            let mut task = TelemetryTask::new(receiver);
            task.run()
        });
        Service {
            keys_plain: KeyGenerator::new(),
            keys_keyed: KeyGenerator::new(),
            sender: sender,
            is_active: Arc::new(AtomicBool::new(is_active)),
        }
    }

    ///
    /// Serialize all histograms as json, in a given format.
    ///
    /// # Panics
    ///
    /// The service will panic if the sender is closed by the time serialization
    /// is complete.
    ///
    pub fn to_json(&self, what: Subset, format: SerializationFormat, sender: Sender<Json>) {
        self.sender.send(Op::Serialize(what, format, sender)).unwrap();
    }

    ///
    /// Make the service (in)active.
    ///
    /// Any data recorded on a histogram while the service is inactive will be ignored.
    ///
    pub fn set_active(&self, value: bool) {
        self.is_active.store(value, Ordering::Relaxed);
    }

    pub fn is_active(&self) -> bool {
        self.is_active.load(Ordering::Relaxed)
    }

    ///
    /// Register a plain histogram, returning a fresh key.
    ///
    fn register_plain(&self, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        let key = self.keys_plain.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterPlain(key.index, named)).unwrap();
        key
    }

    ///
    /// Register a keyed histogram, returning a fresh key.
    ///
    fn register_keyed<T>(&self, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        let key = self.keys_keyed.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterKeyed(key.index, named)).unwrap();
        key
    }
}

/// Upon death of the service, terminate the thread and recollect all
/// owned memory.
impl Drop for Service {
    fn drop(&mut self) {
        let _ = self.sender.send(Op::Terminate);
    }
}

pub struct Service {
    /// A key generator for registration of new plain histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_plain: KeyGenerator<Plain>,

    /// A key generator for registration of new keyed histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_keyed: KeyGenerator<Map>,

    /// A shared boolean that may be turned on/off to (de)activate
    /// Telemetry.
    is_active: Arc<AtomicBool>,

    /// Connection to the thread holding all the storage of this
    /// instance of the service.
    sender: Sender<Op>,
}


// Backstage pass used inside the crate.
impl PrivateAccess {
    pub fn register_plain(service: &Service, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        service.register_plain(name, storage)
    }

    pub fn register_keyed<T>(service: &Service, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        service.register_keyed(name, storage)
    }

    pub fn get_sender(service: &Service) -> &Sender<Op> {
        &service.sender
    }

    pub fn get_is_active(service: &Service) -> &Arc<AtomicBool> {
        &service.is_active
    }
}

pub struct PrivateAccess;