Skip to main content

timely/
worker.rs

1//! The root of each single-threaded worker.
2
3use std::rc::Rc;
4use std::cell::{RefCell, RefMut};
5use std::any::Any;
6use std::str::FromStr;
7use std::time::{Instant, Duration};
8use std::collections::HashMap;
9use std::collections::hash_map::Entry;
10use std::sync::Arc;
11
12use crate::communication::{Allocator, Exchangeable, Push, Pull};
13use crate::communication::allocator::thread::{ThreadPusher, ThreadPuller};
14use crate::scheduling::{Schedule, Activations, Activator, SyncActivator};
15use crate::progress::timestamp::{Refines};
16use crate::progress::SubgraphBuilder;
17use crate::progress::operate::Operate;
18use crate::dataflow::scope::Scope;
19use crate::logging::TimelyLogger;
20
21/// Different ways in which timely's progress tracking can work.
22///
23/// These options drive some buffering and accumulation that timely
24/// can do to try and trade volume of progress traffic against latency.
25/// By accumulating updates longer, a smaller total volume of messages
26/// are sent.
27///
28/// The `ProgressMode::Demand` variant is the most robust, and least
29/// likely to lead to catastrophic performance. The `Eager` variant
30/// is useful for getting the smallest latencies on systems with few
31/// workers, but does risk saturating the system with progress messages
32/// and should be used with care, or not at all.
33///
34/// If you are not certain which option to use, prefer `Demand`, and
35/// perhaps monitor the progress messages through timely's logging
36/// infrastructure to see if their volume is surprisingly high.
37#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
38pub enum ProgressMode {
39    /// Eagerly transmit all progress updates produced by a worker.
40    ///
41    /// Progress messages are transmitted without consideration for the
42    /// possibility that they may unblock other workers. This can result
43    /// in a substantial volume of messages that do not result in a
44    /// change to the lower bound of outstanding work.
45    Eager,
46    /// Delay transmission of progress updates until any could advance
47    /// the global frontier of timestamps.
48    ///
49    /// As timely executes, the progress messages inform each worker of
50    /// the outstanding work remaining in the system. As workers work,
51    /// they produce changes to this outstanding work. This option
52    /// delays the communication of those changes until they might
53    /// possibly cause a change in the lower bound of all outstanding
54    /// work.
55    ///
56    /// The most common case this remedies is when one worker transmits
57    /// messages to other workers, that worker holds a capability for the
58    /// operator and timestamp. Other workers will receive messages, and
59    /// with this option will not immediately acknowledge receiving the
60    /// messages, because the held capability is strictly prior to what
61    /// the messages can affect. Once the capability is released, the
62    /// progress messages are unblocked and transmitted, in accumulated
63    /// form.
64    #[default]
65    Demand,
66}
67
68impl FromStr for ProgressMode {
69    type Err = String;
70
71    fn from_str(s: &str) -> Result<ProgressMode, String> {
72        match s {
73            "eager" => Ok(ProgressMode::Eager),
74            "demand" => Ok(ProgressMode::Demand),
75            _ => Err(format!("unknown progress mode: {}", s)),
76        }
77    }
78}
79
80/// Worker configuration.
81#[derive(Debug, Clone)]
82pub struct Config {
83    /// The progress mode to use.
84    pub(crate) progress_mode: ProgressMode,
85    /// How long an idle worker polls for new events before parking its thread.
86    ///
87    /// Parking a thread and waking it again costs several microseconds, which
88    /// dominates the cost of fine-grained coordination among workers (for
89    /// example, a barrier per iteration of a loop). A worker with nothing to do
90    /// first polls its channels for this long, and parks only if nothing arrives.
91    /// The polling occupies a core, so the duration bounds the CPU an idle worker
92    /// burns each time it goes idle.
93    pub(crate) idle_spin: Duration,
94    /// A map from parameter name to typed parameter values.
95    registry: HashMap<String, Arc<dyn Any + Send + Sync>>,
96}
97
98impl Default for Config {
99    fn default() -> Self {
100        Config {
101            progress_mode: ProgressMode::default(),
102            idle_spin: Duration::from_micros(Self::DEFAULT_IDLE_SPIN_MICROS),
103            registry: HashMap::new(),
104        }
105    }
106}
107
108impl Config {
109    /// The default `idle_spin`, in microseconds.
110    ///
111    /// Parking and unparking a thread costs a few microseconds on common
112    /// platforms, so a worker that polls for this long before parking avoids
113    /// that cost for the short waits typical of tightly coupled workers, at
114    /// the expense of at most this much CPU each time it goes idle.
115    pub const DEFAULT_IDLE_SPIN_MICROS: u64 = 10;
116
117    /// Installs options into a [getopts::Options] struct that correspond
118    /// to the parameters in the configuration.
119    ///
120    /// It is the caller's responsibility to ensure that the installed options
121    /// do not conflict with any other options that may exist in `opts`, or
122    /// that may be installed into `opts` in the future.
123    ///
124    /// This method is only available if the `getopts` feature is enabled, which
125    /// it is by default.
126    #[cfg(feature = "getopts")]
127    pub fn install_options(opts: &mut getopts::Options) {
128        opts.optopt("", "progress-mode", "progress tracking mode (eager or demand)", "MODE");
129        opts.optopt("", "idle-spin", "microseconds an idle worker polls before parking", "MICROS");
130    }
131
132    /// Instantiates a configuration based upon the parsed options in `matches`.
133    ///
134    /// The `matches` object must have been constructed from a
135    /// [getopts::Options] which contained at least the options installed by
136    /// [Self::install_options].
137    ///
138    /// This method is only available if the `getopts` feature is enabled, which
139    /// it is by default.
140    #[cfg(feature = "getopts")]
141    pub fn from_matches(matches: &getopts::Matches) -> Result<Config, String> {
142        let progress_mode = matches
143            .opt_get_default("progress-mode", ProgressMode::Demand)?;
144        let mut config = Config::default().progress_mode(progress_mode);
145        if let Some(micros) = matches.opt_get::<u64>("idle-spin").map_err(|e| e.to_string())? {
146            config = config.idle_spin(Duration::from_micros(micros));
147        }
148        Ok(config)
149    }
150
151    /// Sets the progress mode to `progress_mode`.
152    pub fn progress_mode(mut self, progress_mode: ProgressMode) -> Self {
153        self.progress_mode = progress_mode;
154        self
155    }
156
157    /// Sets how long an idle worker polls for events before parking.
158    pub fn idle_spin(mut self, idle_spin: Duration) -> Self {
159        self.idle_spin = idle_spin;
160        self
161    }
162
163    /// Sets a typed configuration parameter for the given `key`.
164    ///
165    /// It is recommended to install a single configuration struct using a key
166    /// that uniquely identifies your project, to avoid clashes. For example,
167    /// differential dataflow registers a configuration struct under the key
168    /// "differential".
169    ///
170    /// # Examples
171    /// ```rust
172    /// let mut config = timely::Config::process(3);
173    /// config.worker.set("example".to_string(), 7u64);
174    /// timely::execute(config, |worker| {
175    ///    assert_eq!(worker.config().get::<u64>("example"), Some(&7));
176    /// }).unwrap();
177    /// ```
178    pub fn set<T>(&mut self, key: String, val: T) -> &mut Self
179    where
180        T: Send + Sync + 'static,
181    {
182        self.registry.insert(key, Arc::new(val));
183        self
184    }
185
186    /// Gets the value for configured parameter `key`.
187    ///
188    /// Returns `None` if `key` has not previously been set with
189    /// [Config::set], or if the specified `T` does not match the `T`
190    /// from the call to `set`.
191    ///
192    /// # Examples
193    /// ```rust
194    /// let mut config = timely::Config::process(3);
195    /// config.worker.set("example".to_string(), 7u64);
196    /// timely::execute(config, |worker| {
197    ///    assert_eq!(worker.config().get::<u64>("example"), Some(&7));
198    /// }).unwrap();
199    /// ```
200    pub fn get<T: 'static>(&self, key: &str) -> Option<&T> {
201        self.registry.get(key).and_then(|val| val.downcast_ref())
202    }
203}
204
205
206/// A `Worker` is the entry point to a timely dataflow computation. It wraps an `Allocator`
207/// and has a list of dataflows that it manages.
208pub struct Worker {
209    config: Config,
210    /// An optional instant from which the start of the computation should be reckoned.
211    ///
212    /// If this is set to none, system time-based functionality will be unavailable or work badly.
213    /// For example, logging will be unavailable, and activation after a delay will be unavailable.
214    timer: Option<Instant>,
215    paths: Rc<RefCell<HashMap<usize, Rc<[usize]>>>>,
216    allocator: Rc<RefCell<Allocator>>,
217    identifiers: Rc<RefCell<usize>>,
218    // dataflows: Rc<RefCell<Vec<Wrapper>>>,
219    dataflows: Rc<RefCell<HashMap<usize, Wrapper>>>,
220    dataflow_counter: Rc<RefCell<usize>>,
221    logging: Option<Rc<RefCell<crate::logging_core::Registry>>>,
222
223    activations: Rc<RefCell<Activations>>,
224    active_dataflows: Vec<usize>,
225
226    // Temporary storage for channel identifiers during dataflow construction.
227    // These are then associated with a dataflow once constructed.
228    temp_channel_ids: Rc<RefCell<Vec<usize>>>,
229}
230
231
232
233impl Worker {
234    /// Allocates a new `Worker` bound to a channel allocator.
235    pub fn new(config: Config, c: Allocator, now: Option<std::time::Instant>) -> Worker {
236        Worker {
237            config,
238            timer: now,
239            paths:  Default::default(),
240            allocator: Rc::new(RefCell::new(c)),
241            identifiers:  Default::default(),
242            dataflows: Default::default(),
243            dataflow_counter:  Default::default(),
244            logging: now.map(|now| Rc::new(RefCell::new(crate::logging_core::Registry::new(now)))),
245            activations: Rc::new(RefCell::new(Activations::new(now))),
246            active_dataflows: Default::default(),
247            temp_channel_ids:  Default::default(),
248        }
249    }
250
251    /// Performs one step of the computation.
252    ///
253    /// A step gives each dataflow operator a chance to run, and is the
254    /// main way to ensure that a computation proceeds.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// timely::execute_from_args(::std::env::args(), |worker| {
260    ///
261    ///     use timely::dataflow::operators::{ToStream, Inspect};
262    ///
263    ///     worker.dataflow::<usize,_,_>(|scope| {
264    ///         (0 .. 10)
265    ///             .to_stream(scope)
266    ///             .container::<Vec<_>>()
267    ///             .inspect(|x| println!("{:?}", x));
268    ///     });
269    ///
270    ///     worker.step();
271    /// });
272    /// ```
273    pub fn step(&mut self) -> bool {
274        self.step_or_park(Some(Duration::from_secs(0)))
275    }
276
277    /// Performs one step of the computation.
278    ///
279    /// A step gives each dataflow operator a chance to run, and is the
280    /// main way to ensure that a computation proceeds.
281    ///
282    /// This method takes an optional timeout and may park the thread until
283    /// there is work to perform or until this timeout expires. A value of
284    /// `None` allows the worker to park indefinitely, whereas a value of
285    /// `Some(Duration::new(0, 0))` will return without parking the thread.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// timely::execute_from_args(::std::env::args(), |worker| {
291    ///
292    ///     use std::time::Duration;
293    ///     use timely::dataflow::operators::{ToStream, Inspect};
294    ///
295    ///     worker.dataflow::<usize,_,_>(|scope| {
296    ///         (0 .. 10)
297    ///             .to_stream(scope)
298    ///             .container::<Vec<_>>()
299    ///             .inspect(|x| println!("{:?}", x));
300    ///     });
301    ///
302    ///     worker.step_or_park(Some(Duration::from_secs(1)));
303    /// });
304    /// ```
305    pub fn step_or_park(&mut self, duration: Option<Duration>) -> bool {
306
307        // Determine the minimum park duration, where `None` are an absence of a constraint.
308        let mut delay = self.poll_events(duration);
309
310        // An idle worker polls for a while before parking, as parking and
311        // unparking a thread costs more than a short wait usually lasts.
312        if delay != Some(Duration::new(0,0)) {
313            let budget = match delay {
314                Some(delay) => std::cmp::min(delay, self.config.idle_spin),
315                None => self.config.idle_spin,
316            };
317            if budget > Duration::new(0,0) {
318                let start = Instant::now();
319                let mut polls = 0u32;
320                loop {
321                    std::hint::spin_loop();
322                    delay = self.poll_events(duration);
323                    if delay == Some(Duration::new(0,0)) { break; }
324                    // Consult the clock only occasionally, as it is not free.
325                    polls = polls.wrapping_add(1);
326                    if polls % 16 == 0 && start.elapsed() >= budget { break; }
327                }
328            }
329        }
330
331        if delay != Some(Duration::new(0,0)) {
332
333            // Log parking and flush log.
334            if let Some(l) = self.logging().as_mut() {
335                l.log(crate::logging::ParkEvent::park(delay));
336                l.flush();
337            }
338
339            self.allocator
340                .borrow()
341                .await_events(delay);
342
343            // Log return from unpark.
344            self.logging().as_mut().map(|l| l.log(crate::logging::ParkEvent::unpark()));
345        }
346        else {   // Schedule active dataflows.
347
348            let active_dataflows = &mut self.active_dataflows;
349            self.activations
350                .borrow_mut()
351                .for_extensions(&[], |index| active_dataflows.push(index));
352
353            let mut dataflows = self.dataflows.borrow_mut();
354            for index in active_dataflows.drain(..) {
355                // Step dataflow if it exists, remove if not incomplete.
356                if let Entry::Occupied(mut entry) = dataflows.entry(index) {
357                    // TODO: This is a moment at which a scheduling decision is being made.
358                    let incomplete = entry.get_mut().step();
359                    if !incomplete {
360                        let mut paths = self.paths.borrow_mut();
361                        for channel in entry.get_mut().channel_ids.drain(..) {
362                            paths.remove(&channel);
363                        }
364                        entry.remove_entry();
365                    }
366                }
367            }
368        }
369
370        // Clean up, indicate if dataflows remain.
371        self.logging.as_ref().map(|l| l.borrow_mut().flush());
372        self.allocator.borrow_mut().release();
373        !self.dataflows.borrow().is_empty()
374    }
375
376    /// Surfaces channel events as activations, and reports how long the worker may idle.
377    ///
378    /// Returns the minimum of `duration` and the time until the next scheduled
379    /// activation, where `None` is an absence of any constraint, and `Some(0)`
380    /// means there is work to do now.
381    fn poll_events(&self, duration: Option<Duration>) -> Option<Duration> {
382
383        {   // Process channel events. Activate responders.
384            let mut allocator = self.allocator.borrow_mut();
385            allocator.receive();
386            let events = allocator.events();
387            let mut borrow = events.borrow_mut();
388            let paths = self.paths.borrow();
389            borrow.sort_unstable();
390            borrow.dedup();
391            for channel in borrow.drain(..) {
392                // Consider tracking whether a channel
393                // in non-empty, and only activating
394                // on the basis of non-empty channels.
395                // TODO: This is a sloppy way to deal
396                // with channels that may not be alloc'd.
397                if let Some(path) = paths.get(&channel) {
398                    self.activations
399                        .borrow_mut()
400                        .activate(&path[..]);
401                }
402            }
403        }
404
405        // Organize activations.
406        self.activations
407            .borrow_mut()
408            .advance();
409
410        // Consider parking only if we have no pending events, some dataflows, and a non-zero duration.
411        let empty_for = self.activations.borrow().empty_for();
412        match (duration, empty_for) {
413            (Some(x), Some(y)) => Some(std::cmp::min(x,y)),
414            (x, y) => x.or(y),
415        }
416    }
417
418    /// Calls `self.step()` as long as `func` evaluates to `true`.
419    ///
420    /// This method will continually execute even if there is not work
421    /// for the worker to perform. Consider using the similar method
422    /// `Self::step_or_park_while(duration)` to allow the worker to yield
423    /// control if that is appropriate.
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// timely::execute_from_args(::std::env::args(), |worker| {
429    ///
430    ///     use timely::dataflow::operators::{ToStream, Inspect, Probe};
431    ///
432    ///     let probe =
433    ///     worker.dataflow::<usize,_,_>(|scope| {
434    ///         (0 .. 10)
435    ///             .to_stream(scope)
436    ///             .container::<Vec<_>>()
437    ///             .inspect(|x| println!("{:?}", x))
438    ///             .probe()
439    ///             .0
440    ///     });
441    ///
442    ///     worker.step_while(|| probe.less_than(&0));
443    /// });
444    /// ```
445    pub fn step_while<F: FnMut()->bool>(&mut self, func: F) {
446        self.step_or_park_while(Some(Duration::from_secs(0)), func)
447    }
448
449    /// Calls `self.step_or_park(duration)` as long as `func` evaluates to `true`.
450    ///
451    /// This method may yield whenever there is no work to perform, as performed
452    /// by `Self::step_or_park()`. Please consult the documentation for further
453    /// information about that method and its behavior. In particular, the method
454    /// can park the worker indefinitely, if no new work re-awakens the worker.
455    ///
456    /// # Examples
457    ///
458    /// ```
459    /// timely::execute_from_args(::std::env::args(), |worker| {
460    ///
461    ///     use timely::dataflow::operators::{ToStream, Inspect, Probe};
462    ///
463    ///     let probe =
464    ///     worker.dataflow::<usize,_,_>(|scope| {
465    ///         (0 .. 10)
466    ///             .to_stream(scope)
467    ///             .container::<Vec<_>>()
468    ///             .inspect(|x| println!("{:?}", x))
469    ///             .probe()
470    ///             .0
471    ///     });
472    ///
473    ///     worker.step_or_park_while(None, || probe.less_than(&0));
474    /// });
475    /// ```
476    pub fn step_or_park_while<F: FnMut()->bool>(&mut self, duration: Option<Duration>, mut func: F) {
477        while func() { self.step_or_park(duration); }
478    }
479
480    /// The index of the worker out of its peers.
481    ///
482    /// # Examples
483    /// ```
484    /// timely::execute_from_args(::std::env::args(), |worker| {
485    ///
486    ///     let index = worker.index();
487    ///     let peers = worker.peers();
488    ///     let timer = worker.timer().unwrap();
489    ///
490    ///     println!("{:?}\tWorker {} of {}", timer.elapsed(), index, peers);
491    ///
492    /// });
493    /// ```
494    pub fn index(&self) -> usize { self.allocator.borrow().index() }
495    /// The total number of peer workers.
496    ///
497    /// # Examples
498    /// ```
499    /// timely::execute_from_args(::std::env::args(), |worker| {
500    ///
501    ///     let index = worker.index();
502    ///     let peers = worker.peers();
503    ///     let timer = worker.timer().unwrap();
504    ///
505    ///     println!("{:?}\tWorker {} of {}", timer.elapsed(), index, peers);
506    ///
507    /// });
508    /// ```
509    pub fn peers(&self) -> usize { self.allocator.borrow().peers() }
510
511    /// A timer started at the initiation of the timely computation.
512    ///
513    /// # Examples
514    /// ```
515    /// timely::execute_from_args(::std::env::args(), |worker| {
516    ///
517    ///     let index = worker.index();
518    ///     let peers = worker.peers();
519    ///     let timer = worker.timer().unwrap();
520    ///
521    ///     println!("{:?}\tWorker {} of {}", timer.elapsed(), index, peers);
522    ///
523    /// });
524    /// ```
525    pub fn timer(&self) -> Option<Instant> { self.timer }
526
527    /// Allocate a new worker-unique identifier.
528    ///
529    /// This method is public, though it is not expected to be widely used outside
530    /// of the timely dataflow system.
531    pub fn new_identifier(&self) -> usize {
532        *self.identifiers.borrow_mut() += 1;
533        *self.identifiers.borrow() - 1
534    }
535
536    /// The next worker-unique identifier to be allocated.
537    pub fn peek_identifier(&self) -> usize {
538        *self.identifiers.borrow()
539    }
540
541    /// Access to named loggers.
542    ///
543    /// # Examples
544    ///
545    /// ```
546    /// timely::execute_from_args(::std::env::args(), |worker| {
547    ///
548    ///     worker.log_register()
549    ///           .unwrap()
550    ///           .insert::<timely::logging::TimelyEventBuilder,_>("timely", |time, data|
551    ///               println!("{:?}\t{:?}", time, data)
552    ///           );
553    /// });
554    /// ```
555    pub fn log_register(&self) -> Option<RefMut<'_, crate::logging_core::Registry>> {
556        self.logging.as_ref().map(|l| l.borrow_mut())
557    }
558
559    /// Returns the worker configuration parameters.
560    pub fn config(&self) -> &Config { &self.config }
561
562    /// Provides a shared handle to the activation scheduler.
563    pub fn activations(&self) -> Rc<RefCell<Activations>> {
564        Rc::clone(&self.activations)
565    }
566
567    /// Constructs an `Activator` tied to the specified operator address.
568    pub fn activator_for(&self, path: Rc<[usize]>) -> Activator {
569        Activator::new(path, self.activations())
570    }
571
572    /// Constructs a `SyncActivator` tied to the specified operator address.
573    pub fn sync_activator_for(&self, path: Vec<usize>) -> SyncActivator {
574        SyncActivator::new(path, self.activations().borrow().sync())
575    }
576
577    /// Acquires a logger by name, if the log register exists and the name is registered.
578    pub fn logger_for<CB: crate::ContainerBuilder>(&self, name: &str) -> Option<timely_logging::Logger<CB>> {
579        self.log_register().and_then(|l| l.get(name))
580    }
581
582    /// Provides access to the timely logging stream.
583    pub fn logging(&self) -> Option<crate::logging::TimelyLogger> { self.logger_for("timely").map(Into::into) }
584
585    /// Allocates a new channel from a supplied identifier and address.
586    pub fn allocate<D: Exchangeable>(&self, identifier: usize, address: Rc<[usize]>) -> (Vec<Box<dyn Push<D>>>, Box<dyn Pull<D>>) {
587        if address.is_empty() { panic!("Unacceptable address: Length zero"); }
588        let mut paths = self.paths.borrow_mut();
589        paths.insert(identifier, address);
590        self.temp_channel_ids.borrow_mut().push(identifier);
591        self.allocator.borrow_mut().allocate(identifier)
592    }
593
594    /// Constructs a pipeline channel from the worker to itself.
595    pub fn pipeline<T: 'static>(&self, identifier: usize, address: Rc<[usize]>) -> (ThreadPusher<T>, ThreadPuller<T>) {
596        if address.is_empty() { panic!("Unacceptable address: Length zero"); }
597        let mut paths = self.paths.borrow_mut();
598        paths.insert(identifier, address);
599        self.temp_channel_ids.borrow_mut().push(identifier);
600        self.allocator.borrow_mut().pipeline(identifier)
601    }
602
603    /// Allocates a broadcast channel, where each pushed message is received by all.
604    pub fn broadcast<T: Exchangeable + Clone>(&self, identifier: usize, address: Rc<[usize]>) -> (Box<dyn Push<T>>, Box<dyn Pull<T>>) {
605        if address.is_empty() { panic!("Unacceptable address: Length zero"); }
606        let mut paths = self.paths.borrow_mut();
607        paths.insert(identifier, address);
608        self.temp_channel_ids.borrow_mut().push(identifier);
609        self.allocator.borrow_mut().broadcast(identifier)
610    }
611
612    /// Construct a new dataflow.
613    ///
614    /// # Examples
615    /// ```
616    /// timely::execute_from_args(::std::env::args(), |worker| {
617    ///
618    ///     // We must supply the timestamp type here, although
619    ///     // it would generally be determined by type inference.
620    ///     worker.dataflow::<usize,_,_>(|scope| {
621    ///
622    ///         // uses of `scope` to build dataflow
623    ///
624    ///     });
625    /// });
626    /// ```
627    pub fn dataflow<T, R, F>(&mut self, func: F) -> R
628    where
629        T: Refines<()>,
630        F: FnOnce(Scope<T>)->R,
631    {
632        self.dataflow_core("Dataflow", self.logging(), Box::new(()), |_, child| func(child))
633    }
634
635    /// Construct a new dataflow with a (purely cosmetic) name.
636    ///
637    /// # Examples
638    /// ```
639    /// timely::execute_from_args(::std::env::args(), |worker| {
640    ///
641    ///     // We must supply the timestamp type here, although
642    ///     // it would generally be determined by type inference.
643    ///     worker.dataflow_named::<usize,_,_>("Some Dataflow", |scope| {
644    ///
645    ///         // uses of `scope` to build dataflow
646    ///
647    ///     });
648    /// });
649    /// ```
650    pub fn dataflow_named<T, R, F>(&mut self, name: &str, func: F) -> R
651    where
652        T: Refines<()>,
653        F: FnOnce(Scope<T>)->R,
654    {
655        self.dataflow_core(name, self.logging(), Box::new(()), |_, child| func(child))
656    }
657
658    /// Construct a new dataflow with specific configurations.
659    ///
660    /// This method constructs a new dataflow, using a name, logger, and additional
661    /// resources specified as argument. The name is cosmetic, the logger is used to
662    /// handle events generated by the dataflow, and the additional resources are kept
663    /// alive for as long as the dataflow is alive (use case: shared library bindings).
664    ///
665    /// # Examples
666    /// ```
667    /// timely::execute_from_args(::std::env::args(), |worker| {
668    ///
669    ///     // We must supply the timestamp type here, although
670    ///     // it would generally be determined by type inference.
671    ///     worker.dataflow_core::<usize,_,_,_>(
672    ///         "dataflow X",           // Dataflow name
673    ///         None,                   // Optional logger
674    ///         37,                     // Any resources
675    ///         |resources, scope| {    // Closure
676    ///
677    ///             // uses of `resources`, `scope`to build dataflow
678    ///
679    ///         }
680    ///     );
681    /// });
682    /// ```
683    pub fn dataflow_core<T, R, F, V>(&mut self, name: &str, mut logging: Option<TimelyLogger>, mut resources: V, func: F) -> R
684    where
685        T: Refines<()>,
686        F: FnOnce(&mut V, Scope<T>)->R,
687        V: Any+'static,
688    {
689        let dataflow_index = self.allocate_dataflow_index();
690        let addr = vec![dataflow_index].into();
691        let identifier = self.new_identifier();
692
693        let subscope = SubgraphBuilder::new_from(addr, identifier, name);
694        let subscope = RefCell::new(subscope);
695
696        let result = {
697            let builder = Scope {
698                subgraph: &subscope,
699                worker: self,
700            };
701            func(&mut resources, builder)
702        };
703
704        let operator = subscope.into_inner().build(self);
705
706        if let Some(l) = logging.as_mut() {
707            l.log(crate::logging::OperatesEvent {
708                id: identifier,
709                addr: operator.path().to_vec(),
710                name: operator.name().to_string(),
711            });
712            l.flush();
713        }
714
715        let (_, _, operator) = Box::new(operator).initialize();
716
717        let mut temp_channel_ids = self.temp_channel_ids.borrow_mut();
718        let channel_ids = temp_channel_ids.drain(..).collect::<Vec<_>>();
719
720        let wrapper = Wrapper {
721            logging,
722            identifier,
723            operate: Some(operator),
724            resources: Some(Box::new(resources)),
725            channel_ids,
726        };
727        self.dataflows.borrow_mut().insert(dataflow_index, wrapper);
728
729        result
730
731    }
732
733    /// Drops an identified dataflow.
734    ///
735    /// This method removes the identified dataflow, which will no longer be scheduled.
736    /// Various other resources will be cleaned up, though the method is currently in
737    /// public beta rather than expected to work. Please report all crashes and unmet
738    /// expectations!
739    pub fn drop_dataflow(&mut self, dataflow_identifier: usize) {
740        if let Some(mut entry) = self.dataflows.borrow_mut().remove(&dataflow_identifier) {
741            // Garbage collect channel_id to path information.
742            let mut paths = self.paths.borrow_mut();
743            for channel in entry.channel_ids.drain(..) {
744                paths.remove(&channel);
745            }
746        }
747    }
748
749    /// Returns the next index to be used for dataflow construction.
750    ///
751    /// This identifier will appear in the address of contained operators, and can
752    /// be used to drop the dataflow using `self.drop_dataflow()`.
753    pub fn next_dataflow_index(&self) -> usize {
754        *self.dataflow_counter.borrow()
755    }
756
757    /// List the current dataflow indices.
758    pub fn installed_dataflows(&self) -> Vec<usize> {
759        self.dataflows.borrow().keys().cloned().collect()
760    }
761
762    /// Returns `true` if there is at least one dataflow under management.
763    pub fn has_dataflows(&self) -> bool {
764        !self.dataflows.borrow().is_empty()
765    }
766
767    // Acquire a new distinct dataflow identifier.
768    fn allocate_dataflow_index(&self) -> usize {
769        *self.dataflow_counter.borrow_mut() += 1;
770        *self.dataflow_counter.borrow() - 1
771    }
772}
773
774impl Clone for Worker {
775    fn clone(&self) -> Self {
776        Worker {
777            config: self.config.clone(),
778            timer: self.timer,
779            paths: Rc::clone(&self.paths),
780            allocator: Rc::clone(&self.allocator),
781            identifiers: Rc::clone(&self.identifiers),
782            dataflows: Rc::clone(&self.dataflows),
783            dataflow_counter: Rc::clone(&self.dataflow_counter),
784            logging: self.logging.clone(),
785            activations: Rc::clone(&self.activations),
786            active_dataflows: Vec::new(),
787            temp_channel_ids: Rc::clone(&self.temp_channel_ids),
788        }
789    }
790}
791
792struct Wrapper {
793    logging: Option<TimelyLogger>,
794    identifier: usize,
795    operate: Option<Box<dyn Schedule>>,
796    resources: Option<Box<dyn Any>>,
797    channel_ids: Vec<usize>,
798}
799
800impl Wrapper {
801    /// Steps the dataflow, indicates if it remains incomplete.
802    ///
803    /// If the dataflow is incomplete, this call will drop it and its resources,
804    /// dropping the dataflow first and then the resources (so that, e.g., shared
805    /// library bindings will outlive the dataflow).
806    fn step(&mut self) -> bool {
807
808        // Perhaps log information about the start of the schedule call.
809        if let Some(l) = self.logging.as_mut() {
810            l.log(crate::logging::ScheduleEvent::start(self.identifier));
811        }
812
813        let incomplete = {
814            let _current = crate::scheduling::current::enter(self.identifier);
815            self.operate.as_mut().map(|op| op.schedule()).unwrap_or(false)
816        };
817        if !incomplete {
818            self.operate = None;
819            self.resources = None;
820        }
821
822        // Perhaps log information about the stop of the schedule call.
823        if let Some(l) = self.logging.as_mut() {
824            l.log(crate::logging::ScheduleEvent::stop(self.identifier));
825        }
826
827        incomplete
828    }
829}
830
831impl Drop for Wrapper {
832    fn drop(&mut self) {
833        if let Some(l) = self.logging.as_mut() {
834            l.log(crate::logging::ShutdownEvent { id: self.identifier });
835        }
836        // ensure drop order
837        self.operate = None;
838        self.resources = None;
839    }
840}