Skip to main content

timely_communication/allocator/zero_copy/
allocator_process.rs

1//! Zero-copy allocator for intra-process serialized communication.
2
3use std::rc::Rc;
4use std::cell::RefCell;
5use std::collections::{VecDeque, HashMap, hash_map::Entry};
6use std::sync::mpsc::{Sender, Receiver};
7
8use timely_bytes::arc::Bytes;
9
10use crate::networking::MessageHeader;
11
12use crate::{Allocate, Push, Pull};
13use crate::allocator::{AllocateBuilder, Exchangeable, PeerBuilder, Thread};
14use crate::allocator::thread::ThreadPusher;
15use crate::allocator::canary::Canary;
16use crate::allocator::zero_copy::bytes_slab::BytesRefill;
17use crate::allocator::zero_copy::spill::SpillPolicyFn;
18use super::bytes_exchange::{BytesPush, BytesPull, SendEndpoint, MergeQueue};
19
20use super::push_pull::{Pusher, Puller, PullerInner};
21
22/// Builds an instance of a ProcessAllocator.
23///
24/// Builders are required because some of the state in a `ProcessAllocator` cannot be sent between
25/// threads (specifically, the `Rc<RefCell<_>>` local channels). So, we must package up the state
26/// shared between threads here, and then provide a method that will instantiate the non-movable
27/// members once in the destination thread.
28pub struct ProcessBuilder {
29    index:  usize,                      // number out of peers
30    peers:  usize,                      // number of peer allocators.
31    pushers: Vec<Receiver<MergeQueue>>, // for pushing bytes at other workers.
32    pullers: Vec<Sender<MergeQueue>>,   // for pulling bytes from other workers.
33    refill: BytesRefill,
34    spill: Option<SpillPolicyFn>,       // optional spill factory for recv queues.
35}
36
37impl PeerBuilder for ProcessBuilder {
38    type Peer = ProcessBuilder;
39    /// Creates a vector of builders, sharing appropriate state.
40    ///
41    /// This method requires access to a byte exchanger, from which it mints channels.
42    fn new_vector(count: usize, refill: BytesRefill, spill: Option<SpillPolicyFn>) -> Vec<ProcessBuilder> {
43
44        // Channels for the exchange of `MergeQueue` endpoints.
45        let (pullers_vec, pushers_vec) = crate::promise_futures(count, count);
46
47        pushers_vec
48            .into_iter()
49            .zip(pullers_vec)
50            .enumerate()
51            .map(|(index, (pushers, pullers))|
52                ProcessBuilder {
53                    index,
54                    peers: count,
55                    pushers,
56                    pullers,
57                    refill: refill.clone(),
58                    spill: spill.clone(),
59                }
60            )
61            .collect()
62    }
63}
64
65impl ProcessBuilder {
66    /// Builds a `ProcessAllocator`, instantiating `Rc<RefCell<_>>` elements.
67    pub fn build(self) -> ProcessAllocator {
68
69        // Fulfill puller obligations.
70        let mut recvs = Vec::with_capacity(self.peers);
71        for puller in self.pullers.into_iter() {
72            let buzzer = crate::buzzer::Buzzer::default();
73            let (writer, reader) = match self.spill.as_ref() {
74                Some(build_fn) => {
75                    let (w, r) = build_fn();
76                    MergeQueue::new_pair(buzzer, Some(w), Some(r))
77                }
78                None => MergeQueue::new_pair(buzzer, None, None),
79            };
80            // The puller side is the writer here (the producing worker
81            // `extend`s into it); this builder owns the reader handle.
82            recvs.push(reader);
83            puller.send(writer).expect("Failed to send MergeQueue");
84        }
85
86        // Extract pusher commitments.
87        let mut sends = Vec::with_capacity(self.peers);
88        for pusher in self.pushers.into_iter() {
89            let queue = pusher.recv().expect("Failed to receive MergeQueue");
90            let sendpoint = SendEndpoint::new(queue, self.refill.clone());
91            sends.push(Rc::new(RefCell::new(sendpoint)));
92        }
93
94        ProcessAllocator {
95            index: self.index,
96            peers: self.peers,
97            events: Rc::new(RefCell::new(Default::default())),
98            canaries: Rc::new(RefCell::new(Vec::new())),
99            channel_id_bound: None,
100            staged: Vec::new(),
101            sends,
102            recvs,
103            to_local: HashMap::new(),
104            refill: self.refill,
105        }
106    }
107}
108
109impl AllocateBuilder for ProcessBuilder {
110    type Allocator = ProcessAllocator;
111    /// Builds allocator, consumes self.
112    fn build(self) -> Self::Allocator {
113        self.build()
114    }
115
116}
117
118/// A serializing allocator for inter-thread intra-process communication.
119pub struct ProcessAllocator {
120
121    index:      usize,                              // number out of peers
122    peers:      usize,                              // number of peer allocators (for typed channel allocation).
123
124    events: Rc<RefCell<Vec<usize>>>,
125
126    canaries: Rc<RefCell<Vec<usize>>>,
127
128    channel_id_bound: Option<usize>,
129
130    // sending, receiving, and responding to binary buffers.
131    staged:     Vec<Bytes>,
132    sends:      Vec<Rc<RefCell<SendEndpoint<MergeQueue>>>>, // sends[x] -> goes to thread x.
133    recvs:      Vec<MergeQueue>,                            // recvs[x] <- from thread x.
134    to_local:   HashMap<usize, Rc<RefCell<VecDeque<Bytes>>>>,          // to worker-local typed pullers.
135    refill:     BytesRefill,                                // for staging buffers allocated after construction.
136}
137
138/// Delivers each pushed `Bytes` to several destinations, sharing the allocation.
139///
140/// Each destination receives a clone of the handle, a reference count rather
141/// than a copy, through its own endpoint so that ordering and any spill policy
142/// toward that destination are unaffected.
143struct Fanout {
144    targets: Vec<Rc<RefCell<SendEndpoint<MergeQueue>>>>,
145}
146
147impl BytesPush for Fanout {
148    fn extend<I: IntoIterator<Item=Bytes>>(&mut self, iterator: I) {
149        for bytes in iterator {
150            for target in self.targets.iter() {
151                target.borrow_mut().push_bytes(bytes.clone());
152            }
153        }
154    }
155}
156
157/// A pusher that serializes once for all other workers, and hands the element itself to this worker.
158struct BroadcastPusher<T: Exchangeable> {
159    local: ThreadPusher<T>,
160    remote: Option<Pusher<T, Fanout>>,
161}
162
163impl<T: Exchangeable> Push<T> for BroadcastPusher<T> {
164    fn push(&mut self, element: &mut Option<T>) {
165        // The serializing pusher reads the element and leaves it in place.
166        if let Some(remote) = self.remote.as_mut() { remote.push(element); }
167        self.local.push(element);
168    }
169}
170
171impl ProcessAllocator {
172    /// A thread-local queue for messages to this worker, and the puller that
173    /// drains it ahead of the bytes other workers send.
174    ///
175    /// Used for progress broadcasts, whose messages are small and never worth
176    /// paging out. Data channels keep the shared byte queue for self-sends, so
177    /// that a spill policy can apply to them.
178    fn local_channel<T: Exchangeable>(&mut self, identifier: usize) -> (ThreadPusher<T>, Box<dyn Pull<T>>) {
179        let (local_send, local_recv) = Thread::new_from(identifier, Rc::clone(&self.events));
180        let channel = Rc::clone(self.to_local.entry(identifier).or_default());
181        use crate::allocator::counters::Puller as CountPuller;
182        let canary = Canary::new(identifier, Rc::clone(&self.canaries));
183        let puller = PullerInner::new(Box::new(local_recv), channel, canary);
184        let puller = Box::new(CountPuller::new(puller, identifier, Rc::clone(&self.events)));
185        (local_send, puller)
186    }
187}
188
189impl Allocate for ProcessAllocator {
190    fn index(&self) -> usize { self.index }
191    fn peers(&self) -> usize { self.peers }
192    fn allocate<T: Exchangeable>(&mut self, identifier: usize) -> (Vec<Box<dyn Push<T>>>, Box<dyn Pull<T>>) {
193
194        // Assume and enforce in-order identifier allocation.
195        if let Some(bound) = self.channel_id_bound {
196            assert!(bound < identifier);
197        }
198        self.channel_id_bound = Some(identifier);
199
200        let mut pushes = Vec::<Box<dyn Push<T>>>::with_capacity(self.peers());
201
202        for target_index in 0 .. self.peers() {
203
204            // message header template.
205            let header = MessageHeader {
206                channel:    identifier,
207                source:     self.index,
208                target_lower:     target_index,
209                target_upper:     target_index+1,
210                length:     0,
211                seqno:      0,
212            };
213
214            // create, box, and stash new process_binary pusher.
215            pushes.push(Box::new(Pusher::new(header, Rc::clone(&self.sends[target_index]))));
216        }
217
218        let channel = Rc::clone(self.to_local.entry(identifier).or_default());
219
220        use crate::allocator::counters::Puller as CountPuller;
221        let canary = Canary::new(identifier, Rc::clone(&self.canaries));
222        let puller = Box::new(CountPuller::new(Puller::new(channel, canary), identifier, Rc::clone(self.events())));
223
224        (pushes, puller)
225    }
226
227    fn broadcast<T: Exchangeable + Clone>(&mut self, identifier: usize) -> (Box<dyn Push<T>>, Box<dyn Pull<T>>) {
228
229        // Assume and enforce in-order identifier allocation.
230        if let Some(bound) = self.channel_id_bound {
231            assert!(bound < identifier);
232        }
233        self.channel_id_bound = Some(identifier);
234
235        let (local, puller) = self.local_channel::<T>(identifier);
236
237        // Serialize once, and hand every other worker a reference to the bytes.
238        let targets: Vec<_> = (0 .. self.peers).filter(|&target| target != self.index).map(|target| Rc::clone(&self.sends[target])).collect();
239        let remote = if targets.is_empty() { None } else {
240            let header = MessageHeader {
241                channel:        identifier,
242                source:         self.index,
243                target_lower:   0,
244                target_upper:   self.peers,
245                length:         0,
246                seqno:          0,
247            };
248            let endpoint = SendEndpoint::new(Fanout { targets }, self.refill.clone());
249            Some(Pusher::new(header, Rc::new(RefCell::new(endpoint))))
250        };
251
252        (Box::new(BroadcastPusher { local, remote }), puller)
253    }
254
255    // Perform preparatory work, most likely reading binary buffers from self.recv.
256    #[inline(never)]
257    fn receive(&mut self) {
258
259        // Check for channels whose `Puller` has been dropped.
260        let mut canaries = self.canaries.borrow_mut();
261        for dropped_channel in canaries.drain(..) {
262            let _dropped =
263            self.to_local
264                .remove(&dropped_channel)
265                .expect("non-existent channel dropped");
266            // Borrowed channels may be non-empty, if the dataflow was forcibly
267            // dropped. The contract is that if a dataflow is dropped, all other
268            // workers will drop the dataflow too, without blocking indefinitely
269            // on events from it.
270            // assert!(dropped.borrow().is_empty());
271        }
272        std::mem::drop(canaries);
273
274        let mut events = self.events.borrow_mut();
275
276        for recv in self.recvs.iter_mut() {
277            recv.drain_into(&mut self.staged);
278        }
279
280        for mut bytes in self.staged.drain(..) {
281
282            // We expect that `bytes` contains an integral number of messages.
283            // No splitting occurs across allocations.
284            while !bytes.is_empty() {
285
286                if let Some(header) = MessageHeader::try_read(&bytes[..]) {
287
288                    // Get the header and payload, ditch the header.
289                    let mut peel = bytes.extract_to(header.required_bytes());
290                    let _ = peel.extract_to(header.header_bytes());
291
292                    // Increment message count for channel.
293                    // Safe to do this even if the channel has been dropped.
294                    events.push(header.channel);
295
296                    // Ensure that a queue exists.
297                    match self.to_local.entry(header.channel) {
298                        Entry::Vacant(entry) => {
299                            // We may receive data before allocating, and shouldn't block.
300                            if self.channel_id_bound.map(|b| b < header.channel).unwrap_or(true) {
301                                entry.insert(Rc::new(RefCell::new(VecDeque::new())))
302                                    .borrow_mut()
303                                    .push_back(peel);
304                            }
305                        }
306                        Entry::Occupied(mut entry) => {
307                            entry.get_mut().borrow_mut().push_back(peel);
308                        }
309                    }
310                }
311                else {
312                    println!("failed to read full header!");
313                }
314            }
315        }
316    }
317
318    // Perform postparatory work, most likely sending un-full binary buffers.
319    fn release(&mut self) {
320        // Publish outgoing byte ledgers.
321        for send in self.sends.iter_mut() {
322            send.borrow_mut().publish();
323        }
324
325        // OPTIONAL: Tattle on channels sitting on borrowed data.
326        // OPTIONAL: Perhaps copy borrowed data into owned allocation.
327        // for (index, list) in self.to_local.iter() {
328        //     let len = list.borrow_mut().len();
329        //     if len > 0 {
330        //         eprintln!("Warning: worker {}, undrained channel[{}].len() = {}", self.index, index, len);
331        //     }
332        // }
333    }
334
335    fn events(&self) -> &Rc<RefCell<Vec<usize>>> {
336        &self.events
337    }
338    fn await_events(&self, duration: Option<std::time::Duration>) {
339        if self.events.borrow().is_empty() {
340            if let Some(duration) = duration {
341                std::thread::park_timeout(duration);
342            }
343            else {
344                std::thread::park();
345            }
346        }
347    }
348}