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}