Skip to main content

matrix_sdk_common/linked_chunk/
updates.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    collections::HashMap,
17    pin::Pin,
18    sync::{Arc, RwLock, Weak},
19    task::{Context, Poll, Waker},
20};
21
22use futures_core::Stream;
23
24use super::{ChunkIdentifier, Position};
25
26/// Represent the updates that have happened inside a [`LinkedChunk`].
27///
28/// To retrieve the updates, use [`LinkedChunk::updates`].
29///
30/// These updates are useful to store a `LinkedChunk` in another form of
31/// storage, like a database or something similar.
32///
33/// [`LinkedChunk`]: super::LinkedChunk
34/// [`LinkedChunk::updates`]: super::LinkedChunk::updates
35#[derive(Debug, Clone, PartialEq)]
36pub enum Update<Item, Gap> {
37    /// A new chunk of kind Items has been created.
38    NewItemsChunk {
39        /// The identifier of the previous chunk of this new chunk.
40        previous: Option<ChunkIdentifier>,
41
42        /// The identifier of the new chunk.
43        new: ChunkIdentifier,
44
45        /// The identifier of the next chunk of this new chunk.
46        next: Option<ChunkIdentifier>,
47    },
48
49    /// A new chunk of kind Gap has been created.
50    NewGapChunk {
51        /// The identifier of the previous chunk of this new chunk.
52        previous: Option<ChunkIdentifier>,
53
54        /// The identifier of the new chunk.
55        new: ChunkIdentifier,
56
57        /// The identifier of the next chunk of this new chunk.
58        next: Option<ChunkIdentifier>,
59
60        /// The content of the chunk.
61        gap: Gap,
62    },
63
64    /// A chunk has been removed.
65    RemoveChunk(ChunkIdentifier),
66
67    /// Items are pushed inside a chunk of kind Items.
68    PushItems {
69        /// The [`Position`] of the items.
70        ///
71        /// This value is given to prevent the need for position computations by
72        /// the update readers. Items are pushed, so the positions should be
73        /// incrementally computed from the previous items, which requires the
74        /// reading of the last previous item. With `at`, the update readers no
75        /// longer need to do so.
76        at: Position,
77
78        /// The items.
79        items: Vec<Item>,
80    },
81
82    /// An item has been replaced in the linked chunk.
83    ///
84    /// The `at` position MUST resolve to the actual position an existing *item*
85    /// (not a gap).
86    ReplaceItem {
87        /// The position of the item that's being replaced.
88        at: Position,
89
90        /// The new value for the item.
91        item: Item,
92    },
93
94    /// An item has been removed inside a chunk of kind Items.
95    RemoveItem {
96        /// The [`Position`] of the item.
97        at: Position,
98    },
99
100    /// The last items of a chunk have been detached, i.e. the chunk has been
101    /// truncated.
102    DetachLastItems {
103        /// The split position. Before this position (`..position`), items are
104        /// kept, from this position (`position..`), items are
105        /// detached.
106        at: Position,
107    },
108
109    /// Detached items (see [`Self::DetachLastItems`]) starts being reattached.
110    StartReattachItems,
111
112    /// Reattaching items (see [`Self::StartReattachItems`]) is finished.
113    EndReattachItems,
114
115    /// All chunks have been cleared, i.e. all items and all gaps have been
116    /// dropped.
117    Clear,
118}
119
120impl<Item, Gap> Update<Item, Gap> {
121    /// Get the items from the [`Update`] if any.
122    ///
123    /// This function is useful if you only care about the items from the
124    /// [`Update`] and not what kind of update it was and where the items
125    /// should be placed.
126    ///
127    /// [`Update`] variants which don't contain any items will return an empty
128    /// [`Vec`].
129    pub fn into_items(self) -> Vec<Item> {
130        match self {
131            Update::NewItemsChunk { .. }
132            | Update::NewGapChunk { .. }
133            | Update::RemoveChunk(_)
134            | Update::RemoveItem { .. }
135            | Update::DetachLastItems { .. }
136            | Update::StartReattachItems
137            | Update::EndReattachItems
138            | Update::Clear => vec![],
139            Update::PushItems { items, .. } => items,
140            Update::ReplaceItem { item, .. } => vec![item],
141        }
142    }
143}
144
145/// A collection of [`Update`]s that can be observed.
146///
147/// Get a value for this type with [`LinkedChunk::updates`].
148///
149/// All clones of this type share the same data.
150///
151/// [`LinkedChunk::updates`]: super::LinkedChunk::updates
152#[derive(Debug)]
153pub struct ObservableUpdates<Item, Gap> {
154    pub(super) inner: Arc<RwLock<UpdatesInner<Item, Gap>>>,
155}
156
157impl<Item, Gap> ObservableUpdates<Item, Gap> {
158    /// Create a new [`ObservableUpdates`].
159    pub(super) fn new() -> Self {
160        Self { inner: Arc::new(RwLock::new(UpdatesInner::new())) }
161    }
162
163    /// Push a new update.
164    pub(super) fn push(&mut self, update: Update<Item, Gap>) {
165        self.inner.write().unwrap().push(update);
166    }
167
168    /// Clear all pending updates.
169    pub(super) fn clear_pending(&mut self) {
170        self.inner.write().unwrap().clear_pending();
171    }
172
173    /// Take new updates.
174    ///
175    /// Updates that have been taken will not be read again.
176    pub fn take(&mut self) -> Vec<Update<Item, Gap>>
177    where
178        Item: Clone,
179        Gap: Clone,
180    {
181        self.inner.write().unwrap().take().to_owned()
182    }
183
184    /// Subscribe to updates by using a [`Stream`].
185    pub fn subscribe(&mut self) -> UpdatesSubscriber<Item, Gap> {
186        // A subscriber is a new update reader, it needs its own token.
187        let token = self.new_reader_token();
188
189        UpdatesSubscriber::new(Arc::downgrade(&self.inner), token)
190    }
191
192    /// Generate a new [`ReaderToken`].
193    pub(super) fn new_reader_token(&mut self) -> ReaderToken {
194        let mut inner = self.inner.write().unwrap();
195
196        // Add 1 before reading the `last_token`, in this particular order, because the
197        // 0 token is reserved by `MAIN_READER_TOKEN`.
198        inner.last_token += 1;
199        let last_token = inner.last_token;
200
201        inner.last_index_per_reader.insert(last_token, 0);
202
203        last_token
204    }
205
206    /// Create a new [`ObservableUpdatesPusher`], privately.
207    pub(super) fn new_pusher(&self) -> ObservableUpdatesPusher<Item, Gap> {
208        ObservableUpdatesPusher { inner: self.inner.clone() }
209    }
210}
211
212/// This type is similar to [`ObservableUpdates`] except it has a single `push`
213/// method which takes a `&self` instead of a `&mut self` to accommodate a
214/// particular need in `Ends` for lazily get the first chunk.
215pub(super) struct ObservableUpdatesPusher<Item, Gap> {
216    inner: Arc<RwLock<UpdatesInner<Item, Gap>>>,
217}
218
219impl<Item, Gap> ObservableUpdatesPusher<Item, Gap> {
220    /// Push a new update, even if `&self` while we could expect a `&mut self`.
221    pub fn push(&self, update: Update<Item, Gap>) {
222        self.inner.write().unwrap().push(update);
223    }
224}
225
226/// A token used to represent readers that read the updates in
227/// [`UpdatesInner`].
228pub(super) type ReaderToken = usize;
229
230/// Inner type for [`ObservableUpdates`].
231///
232/// The particularity of this type is that multiple readers can read the
233/// updates. A reader has a [`ReaderToken`]. The public API (i.e.
234/// [`ObservableUpdates`]) is considered to be the _main reader_ (it has the
235/// token [`Self::MAIN_READER_TOKEN`]).
236///
237/// An update that have been read by all readers are garbage collected to be
238/// removed from the memory. An update will never be read twice by the same
239/// reader.
240///
241/// Why do we need multiple readers? The public API reads the updates with
242/// [`ObservableUpdates::take`], but the private API must also read the updates
243/// for example with [`UpdatesSubscriber`]. Of course, they can be multiple
244/// `UpdatesSubscriber`s at the same time. Hence the need of supporting multiple
245/// readers.
246#[derive(Debug)]
247pub(super) struct UpdatesInner<Item, Gap> {
248    /// All the updates that have not been read by all readers.
249    updates: Vec<Update<Item, Gap>>,
250
251    /// Updates are stored in [`Self::updates`]. Multiple readers can read them.
252    /// A reader is identified by a [`ReaderToken`].
253    ///
254    /// To each reader token is associated an index that represents the index of
255    /// the last reading. It is used to never return the same update twice.
256    last_index_per_reader: HashMap<ReaderToken, usize>,
257
258    /// The last generated token. This is useful to generate new token.
259    last_token: ReaderToken,
260
261    /// Pending wakers for [`UpdateSubscriber`]s. A waker is removed
262    /// every time it is called.
263    wakers: Vec<Waker>,
264}
265
266impl<Item, Gap> UpdatesInner<Item, Gap> {
267    /// The token used by the main reader. See [`Self::take`] to learn more.
268    const MAIN_READER_TOKEN: ReaderToken = 0;
269
270    /// Create a new [`Self`].
271    fn new() -> Self {
272        Self {
273            updates: Vec::with_capacity(8),
274            last_index_per_reader: {
275                let mut map = HashMap::with_capacity(2);
276                map.insert(Self::MAIN_READER_TOKEN, 0);
277
278                map
279            },
280            last_token: Self::MAIN_READER_TOKEN,
281            wakers: Vec::with_capacity(2),
282        }
283    }
284
285    /// Push a new update.
286    fn push(&mut self, update: Update<Item, Gap>) {
287        self.updates.push(update);
288
289        // Wake them up \o/.
290        for waker in self.wakers.drain(..) {
291            waker.wake();
292        }
293    }
294
295    /// Clear all pending updates.
296    fn clear_pending(&mut self) {
297        self.updates.clear();
298
299        // Reset all the per-reader indices.
300        for idx in self.last_index_per_reader.values_mut() {
301            *idx = 0;
302        }
303
304        // No need to wake the wakers; they're waiting for a new update, and we
305        // just made them all disappear.
306    }
307
308    /// Take new updates; it considers the caller is the main reader, i.e. it
309    /// will use the [`Self::MAIN_READER_TOKEN`].
310    ///
311    /// Updates that have been read will never be read again by the current
312    /// reader.
313    ///
314    /// Learn more by reading [`Self::take_with_token`].
315    fn take(&mut self) -> &[Update<Item, Gap>] {
316        self.take_with_token(Self::MAIN_READER_TOKEN)
317    }
318
319    /// Take new updates with a particular reader token.
320    ///
321    /// Updates are stored in [`Self::updates`]. Multiple readers can read them.
322    /// A reader is identified by a [`ReaderToken`]. Every reader can
323    /// take/read/consume each update only once. An internal index is stored
324    /// per reader token to know where to start reading updates next time this
325    /// method is called.
326    pub(super) fn take_with_token(&mut self, token: ReaderToken) -> &[Update<Item, Gap>] {
327        // Let's garbage collect unused updates.
328        self.garbage_collect();
329
330        let index = self
331            .last_index_per_reader
332            .get_mut(&token)
333            .expect("Given `UpdatesToken` does not map to any index");
334
335        // Read new updates, and update the index.
336        let slice = &self.updates[*index..];
337        *index = self.updates.len();
338
339        slice
340    }
341
342    /// Has the given reader, identified by its [`ReaderToken`], some pending
343    /// updates, or has it consumed all the pending updates?
344    pub(super) fn is_reader_up_to_date(&self, token: ReaderToken) -> bool {
345        *self.last_index_per_reader.get(&token).expect("unknown reader token") == self.updates.len()
346    }
347
348    /// Return the number of updates in the buffer.
349    #[cfg(test)]
350    fn len(&self) -> usize {
351        self.updates.len()
352    }
353
354    /// Garbage collect unused updates. An update is considered unused when it's
355    /// been read by all readers.
356    ///
357    /// Basically, it reduces to finding the smallest last index for all
358    /// readers, and clear from 0 to that index.
359    fn garbage_collect(&mut self) {
360        let min_index = self.last_index_per_reader.values().min().copied().unwrap_or(0);
361
362        if min_index > 0 {
363            let _ = self.updates.drain(0..min_index);
364
365            // Let's shift the indices to the left by `min_index` to preserve them.
366            for index in self.last_index_per_reader.values_mut() {
367                *index -= min_index;
368            }
369        }
370    }
371}
372
373/// A subscriber to [`ObservableUpdates`]. It is helpful to receive updates via
374/// a [`Stream`].
375#[derive(Debug)]
376pub struct UpdatesSubscriber<Item, Gap> {
377    /// Weak reference to [`UpdatesInner`].
378    ///
379    /// Using a weak reference allows [`ObservableUpdates`] to be dropped
380    /// freely even if a subscriber exists.
381    updates: Weak<RwLock<UpdatesInner<Item, Gap>>>,
382
383    /// The token to read the updates.
384    token: ReaderToken,
385}
386
387impl<Item, Gap> UpdatesSubscriber<Item, Gap> {
388    /// Create a new [`Self`].
389    fn new(updates: Weak<RwLock<UpdatesInner<Item, Gap>>>, token: ReaderToken) -> Self {
390        Self { updates, token }
391    }
392}
393
394impl<Item, Gap> Stream for UpdatesSubscriber<Item, Gap>
395where
396    Item: Clone,
397    Gap: Clone,
398{
399    type Item = Vec<Update<Item, Gap>>;
400
401    fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
402        let Some(updates) = self.updates.upgrade() else {
403            // The `ObservableUpdates` has been dropped. It's time to close this stream.
404            return Poll::Ready(None);
405        };
406
407        let mut updates = updates.write().unwrap();
408        let the_updates = updates.take_with_token(self.token);
409
410        // No updates.
411        if the_updates.is_empty() {
412            // Let's register the waker.
413            updates.wakers.push(context.waker().clone());
414
415            // The stream is pending.
416            return Poll::Pending;
417        }
418
419        // There is updates! Let's forward them in this stream.
420        Poll::Ready(Some(the_updates.to_owned()))
421    }
422}
423
424impl<Item, Gap> Drop for UpdatesSubscriber<Item, Gap> {
425    fn drop(&mut self) {
426        // Remove `Self::token` from `UpdatesInner::last_index_per_reader`.
427        // This is important so that the garbage collector can do its jobs correctly
428        // without a dead dangling reader token.
429        if let Some(updates) = self.updates.upgrade() {
430            let mut updates = updates.write().unwrap();
431
432            // Remove the reader token from `UpdatesInner`.
433            // It's safe to ignore the result of `remove` here: `None` means the token was
434            // already removed (note: it should be unreachable).
435            let _ = updates.last_index_per_reader.remove(&self.token);
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use std::{
443        sync::{Arc, Mutex},
444        task::{Context, Poll, Wake},
445    };
446
447    use assert_matches::assert_matches;
448    use futures_core::Stream;
449    use futures_util::pin_mut;
450
451    use super::{super::LinkedChunk, ChunkIdentifier, Position, UpdatesInner};
452    use crate::linked_chunk::Update;
453
454    #[test]
455    fn test_updates_take_and_garbage_collector() {
456        use super::Update::*;
457
458        let mut linked_chunk = LinkedChunk::<10, char, ()>::new_with_update_history();
459
460        // Simulate another updates “reader”, it can a subscriber.
461        let main_token = UpdatesInner::<char, ()>::MAIN_READER_TOKEN;
462        let other_token = {
463            let updates = linked_chunk.updates().unwrap();
464            let mut inner = updates.inner.write().unwrap();
465            inner.last_token += 1;
466
467            let other_token = inner.last_token;
468            inner.last_index_per_reader.insert(other_token, 0);
469
470            other_token
471        };
472
473        // Let's trigger the chunk creation to simplify the test.
474        let _ = linked_chunk.first_chunk();
475
476        // There is an update.
477        {
478            let updates = linked_chunk.updates().unwrap();
479
480            assert_eq!(
481                updates.take(),
482                &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }],
483            );
484            assert_eq!(
485                updates.inner.write().unwrap().take_with_token(other_token),
486                &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }],
487            );
488        }
489
490        // No new update.
491        {
492            let updates = linked_chunk.updates().unwrap();
493
494            assert!(updates.take().is_empty());
495            assert!(updates.inner.write().unwrap().take_with_token(other_token).is_empty());
496        }
497
498        linked_chunk.push_items_back(['a']);
499        linked_chunk.push_items_back(['b']);
500        linked_chunk.push_items_back(['c']);
501
502        // Scenario 1: “main” takes the new updates, “other” doesn't take the new
503        // updates.
504        //
505        // 0   1   2   3
506        // +---+---+---+
507        // | a | b | c |
508        // +---+---+---+
509        //
510        // “main” will move its index from 0 to 3.
511        // “other” won't move its index.
512        {
513            let updates = linked_chunk.updates().unwrap();
514
515            {
516                // Inspect number of updates in memory.
517                assert_eq!(updates.inner.read().unwrap().len(), 3);
518            }
519
520            assert_eq!(
521                updates.take(),
522                &[
523                    PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] },
524                    PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b'] },
525                    PushItems { at: Position(ChunkIdentifier(0), 2), items: vec!['c'] },
526                ]
527            );
528
529            {
530                let inner = updates.inner.read().unwrap();
531
532                // Inspect number of updates in memory.
533                // It must be the same number as before as the garbage collector weren't not
534                // able to remove any unused updates.
535                assert_eq!(inner.len(), 3);
536
537                // Inspect the indices.
538                let indices = &inner.last_index_per_reader;
539
540                assert_eq!(indices.get(&main_token), Some(&3));
541                assert_eq!(indices.get(&other_token), Some(&0));
542            }
543        }
544
545        linked_chunk.push_items_back(['d']);
546        linked_chunk.push_items_back(['e']);
547        linked_chunk.push_items_back(['f']);
548
549        // Scenario 2: “other“ takes the new updates, “main” doesn't take the
550        // new updates.
551        //
552        // 0   1   2   3   4   5   6
553        // +---+---+---+---+---+---+
554        // | a | b | c | d | e | f |
555        // +---+---+---+---+---+---+
556        //
557        // “main” won't move its index.
558        // “other” will move its index from 0 to 6.
559        {
560            let updates = linked_chunk.updates().unwrap();
561
562            assert_eq!(
563                updates.inner.write().unwrap().take_with_token(other_token),
564                &[
565                    PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] },
566                    PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b'] },
567                    PushItems { at: Position(ChunkIdentifier(0), 2), items: vec!['c'] },
568                    PushItems { at: Position(ChunkIdentifier(0), 3), items: vec!['d'] },
569                    PushItems { at: Position(ChunkIdentifier(0), 4), items: vec!['e'] },
570                    PushItems { at: Position(ChunkIdentifier(0), 5), items: vec!['f'] },
571                ]
572            );
573
574            {
575                let inner = updates.inner.read().unwrap();
576
577                // Inspect number of updates in memory.
578                // It must be the same number as before as the garbage collector will be able to
579                // remove unused updates but at the next call…
580                assert_eq!(inner.len(), 6);
581
582                // Inspect the indices.
583                let indices = &inner.last_index_per_reader;
584
585                assert_eq!(indices.get(&main_token), Some(&3));
586                assert_eq!(indices.get(&other_token), Some(&6));
587            }
588        }
589
590        // Scenario 3: “other” take new updates, but there is none, “main”
591        // doesn't take new updates. The garbage collector will run and collect
592        // unused updates.
593        //
594        // 0   1   2   3
595        // +---+---+---+
596        // | d | e | f |
597        // +---+---+---+
598        //
599        // “main” will have its index updated from 3 to 0.
600        // “other” will have its index updated from 6 to 3.
601        {
602            let updates = linked_chunk.updates().unwrap();
603
604            assert!(updates.inner.write().unwrap().take_with_token(other_token).is_empty());
605
606            {
607                let inner = updates.inner.read().unwrap();
608
609                // Inspect number of updates in memory.
610                // The garbage collector has removed unused updates.
611                assert_eq!(inner.len(), 3);
612
613                // Inspect the indices. They must have been adjusted.
614                let indices = &inner.last_index_per_reader;
615
616                assert_eq!(indices.get(&main_token), Some(&0));
617                assert_eq!(indices.get(&other_token), Some(&3));
618            }
619        }
620
621        linked_chunk.push_items_back(['g']);
622        linked_chunk.push_items_back(['h']);
623        linked_chunk.push_items_back(['i']);
624
625        // Scenario 4: both “main” and “other” take the new updates.
626        //
627        // 0   1   2   3   4   5   6
628        // +---+---+---+---+---+---+
629        // | d | e | f | g | h | i |
630        // +---+---+---+---+---+---+
631        //
632        // “main” will have its index updated from 0 to 3.
633        // “other” will have its index updated from 6 to 3.
634        {
635            let updates = linked_chunk.updates().unwrap();
636
637            assert_eq!(
638                updates.take(),
639                &[
640                    PushItems { at: Position(ChunkIdentifier(0), 3), items: vec!['d'] },
641                    PushItems { at: Position(ChunkIdentifier(0), 4), items: vec!['e'] },
642                    PushItems { at: Position(ChunkIdentifier(0), 5), items: vec!['f'] },
643                    PushItems { at: Position(ChunkIdentifier(0), 6), items: vec!['g'] },
644                    PushItems { at: Position(ChunkIdentifier(0), 7), items: vec!['h'] },
645                    PushItems { at: Position(ChunkIdentifier(0), 8), items: vec!['i'] },
646                ]
647            );
648            assert_eq!(
649                updates.inner.write().unwrap().take_with_token(other_token),
650                &[
651                    PushItems { at: Position(ChunkIdentifier(0), 6), items: vec!['g'] },
652                    PushItems { at: Position(ChunkIdentifier(0), 7), items: vec!['h'] },
653                    PushItems { at: Position(ChunkIdentifier(0), 8), items: vec!['i'] },
654                ]
655            );
656
657            {
658                let inner = updates.inner.read().unwrap();
659
660                // Inspect number of updates in memory.
661                // The garbage collector had a chance to collect the first 3 updates.
662                assert_eq!(inner.len(), 3);
663
664                // Inspect the indices.
665                let indices = &inner.last_index_per_reader;
666
667                assert_eq!(indices.get(&main_token), Some(&3));
668                assert_eq!(indices.get(&other_token), Some(&3));
669            }
670        }
671
672        // Scenario 5: no more updates but they both try to take new updates.
673        // The garbage collector will collect all updates as all of them as
674        // been read already.
675        //
676        // “main” will have its index updated from 0 to 0.
677        // “other” will have its index updated from 3 to 0.
678        {
679            let updates = linked_chunk.updates().unwrap();
680
681            assert!(updates.take().is_empty());
682            assert!(updates.inner.write().unwrap().take_with_token(other_token).is_empty());
683
684            {
685                let inner = updates.inner.read().unwrap();
686
687                // Inspect number of updates in memory.
688                // The garbage collector had a chance to collect all updates.
689                assert_eq!(inner.len(), 0);
690
691                // Inspect the indices.
692                let indices = &inner.last_index_per_reader;
693
694                assert_eq!(indices.get(&main_token), Some(&0));
695                assert_eq!(indices.get(&other_token), Some(&0));
696            }
697        }
698    }
699
700    struct CounterWaker {
701        number_of_wakeup: Mutex<usize>,
702    }
703
704    impl Wake for CounterWaker {
705        fn wake(self: Arc<Self>) {
706            *self.number_of_wakeup.lock().unwrap() += 1;
707        }
708    }
709
710    #[test]
711    fn test_updates_stream() {
712        use super::Update::*;
713
714        let counter_waker = Arc::new(CounterWaker { number_of_wakeup: Mutex::new(0) });
715        let waker = counter_waker.clone().into();
716        let mut context = Context::from_waker(&waker);
717
718        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
719
720        let updates_subscriber = linked_chunk.updates().unwrap().subscribe();
721        pin_mut!(updates_subscriber);
722
723        // No initial update, stream is pending.
724        assert_matches!(updates_subscriber.as_mut().poll_next(&mut context), Poll::Pending);
725        assert_eq!(*counter_waker.number_of_wakeup.lock().unwrap(), 0);
726
727        // Let's generate an update.
728        linked_chunk.push_items_back(['a']);
729
730        // The waker must have been called.
731        assert_eq!(*counter_waker.number_of_wakeup.lock().unwrap(), 1);
732
733        // There is an update! Right after that, the stream is pending again.
734        assert_matches!(
735            updates_subscriber.as_mut().poll_next(&mut context),
736            Poll::Ready(Some(items)) => {
737                assert_eq!(
738                    items,
739                    &[
740                        NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
741                        PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
742                    ]
743                );
744            }
745        );
746        assert_matches!(updates_subscriber.as_mut().poll_next(&mut context), Poll::Pending);
747
748        // Let's generate two other updates.
749        linked_chunk.push_items_back(['b']);
750        linked_chunk.push_items_back(['c']);
751
752        // The waker must have been called only once for the two updates.
753        assert_eq!(*counter_waker.number_of_wakeup.lock().unwrap(), 2);
754
755        // We can consume the updates without the stream, but the stream continues to
756        // know it has updates.
757        assert_eq!(
758            linked_chunk.updates().unwrap().take(),
759            &[
760                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
761                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] },
762                PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b'] },
763                PushItems { at: Position(ChunkIdentifier(0), 2), items: vec!['c'] },
764            ]
765        );
766        assert_matches!(
767            updates_subscriber.as_mut().poll_next(&mut context),
768            Poll::Ready(Some(items)) => {
769                assert_eq!(
770                    items,
771                    &[
772                        PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b'] },
773                        PushItems { at: Position(ChunkIdentifier(0), 2), items: vec!['c'] },
774                    ]
775                );
776            }
777        );
778        assert_matches!(updates_subscriber.as_mut().poll_next(&mut context), Poll::Pending);
779
780        // When dropping the `LinkedChunk`, it closes the stream.
781        drop(linked_chunk);
782        assert_matches!(updates_subscriber.as_mut().poll_next(&mut context), Poll::Ready(None));
783
784        // Wakers calls have not changed.
785        assert_eq!(*counter_waker.number_of_wakeup.lock().unwrap(), 2);
786    }
787
788    #[test]
789    fn test_updates_multiple_streams() {
790        use super::Update::*;
791
792        let counter_waker1 = Arc::new(CounterWaker { number_of_wakeup: Mutex::new(0) });
793        let counter_waker2 = Arc::new(CounterWaker { number_of_wakeup: Mutex::new(0) });
794
795        let waker1 = counter_waker1.clone().into();
796        let waker2 = counter_waker2.clone().into();
797
798        let mut context1 = Context::from_waker(&waker1);
799        let mut context2 = Context::from_waker(&waker2);
800
801        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
802
803        let updates_subscriber1 = linked_chunk.updates().unwrap().subscribe();
804        pin_mut!(updates_subscriber1);
805
806        // Scope for `updates_subscriber2`.
807        let updates_subscriber2_token = {
808            let updates_subscriber2 = linked_chunk.updates().unwrap().subscribe();
809            pin_mut!(updates_subscriber2);
810
811            // No initial updates, streams are pending.
812            assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Pending);
813            assert_eq!(*counter_waker1.number_of_wakeup.lock().unwrap(), 0);
814
815            assert_matches!(updates_subscriber2.as_mut().poll_next(&mut context2), Poll::Pending);
816            assert_eq!(*counter_waker2.number_of_wakeup.lock().unwrap(), 0);
817
818            // Let's generate an update.
819            linked_chunk.push_items_back(['a']);
820
821            // The wakers must have been called.
822            assert_eq!(*counter_waker1.number_of_wakeup.lock().unwrap(), 1);
823            assert_eq!(*counter_waker2.number_of_wakeup.lock().unwrap(), 1);
824
825            // There is an update! Right after that, the streams are pending again.
826            assert_matches!(
827                updates_subscriber1.as_mut().poll_next(&mut context1),
828                Poll::Ready(Some(items)) => {
829                    assert_eq!(
830                        items,
831                        &[
832                            NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
833                            PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
834                        ]
835                    );
836                }
837            );
838            assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Pending);
839            assert_matches!(
840                updates_subscriber2.as_mut().poll_next(&mut context2),
841                Poll::Ready(Some(items)) => {
842                    assert_eq!(
843                        items,
844                        &[
845                            NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
846                            PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
847                        ]
848                    );
849                }
850            );
851            assert_matches!(updates_subscriber2.as_mut().poll_next(&mut context2), Poll::Pending);
852
853            // Let's generate two other updates.
854            linked_chunk.push_items_back(['b']);
855            linked_chunk.push_items_back(['c']);
856
857            // A waker is consumed when called. The first call to `push_items_back` will
858            // call and consume the wakers. The second call to `push_items_back` will do
859            // nothing as the wakers have been consumed. New wakers will be registered on
860            // polling.
861            //
862            // So, the waker must have been called only once for the two updates.
863            assert_eq!(*counter_waker1.number_of_wakeup.lock().unwrap(), 2);
864            assert_eq!(*counter_waker2.number_of_wakeup.lock().unwrap(), 2);
865
866            // Let's poll `updates_subscriber1` only.
867            assert_matches!(
868                updates_subscriber1.as_mut().poll_next(&mut context1),
869                Poll::Ready(Some(items)) => {
870                    assert_eq!(
871                        items,
872                        &[
873                            PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b'] },
874                            PushItems { at: Position(ChunkIdentifier(0), 2), items: vec!['c'] },
875                        ]
876                    );
877                }
878            );
879            assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Pending);
880
881            // For the sake of this test, we also need to advance the main reader token.
882            let _ = linked_chunk.updates().unwrap().take();
883            let _ = linked_chunk.updates().unwrap().take();
884
885            // If we inspect the garbage collector state, `a`, `b` and `c` should still be
886            // present because not all of them have been consumed by `updates_subscriber2`
887            // yet.
888            {
889                let updates = linked_chunk.updates().unwrap();
890
891                let inner = updates.inner.read().unwrap();
892
893                // Inspect number of updates in memory.
894                // We get 2 because the garbage collector runs before data are taken, not after:
895                // `updates_subscriber2` has read `a` only, so `b` and `c` remain.
896                assert_eq!(inner.len(), 2);
897
898                // Inspect the indices.
899                let indices = &inner.last_index_per_reader;
900
901                assert_eq!(indices.get(&updates_subscriber1.token), Some(&2));
902                assert_eq!(indices.get(&updates_subscriber2.token), Some(&0));
903            }
904
905            // Poll `updates_subscriber1` again: there is no new update so it must be
906            // pending.
907            assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Pending);
908
909            // The state of the garbage collector is unchanged: `a`, `b` and `c` are still
910            // in memory.
911            {
912                let updates = linked_chunk.updates().unwrap();
913
914                let inner = updates.inner.read().unwrap();
915
916                // Inspect number of updates in memory. Value is unchanged.
917                assert_eq!(inner.len(), 2);
918
919                // Inspect the indices. They are unchanged.
920                let indices = &inner.last_index_per_reader;
921
922                assert_eq!(indices.get(&updates_subscriber1.token), Some(&2));
923                assert_eq!(indices.get(&updates_subscriber2.token), Some(&0));
924            }
925
926            updates_subscriber2.token
927            // Drop `updates_subscriber2`!
928        };
929
930        // `updates_subscriber2` has been dropped. Poll `updates_subscriber1` again:
931        // still no new update, but it will run the garbage collector again, and this
932        // time `updates_subscriber2` is not “retaining” `b` and `c`. The garbage
933        // collector must be empty.
934        assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Pending);
935
936        // Inspect the garbage collector.
937        {
938            let updates = linked_chunk.updates().unwrap();
939
940            let inner = updates.inner.read().unwrap();
941
942            // Inspect number of updates in memory.
943            assert_eq!(inner.len(), 0);
944
945            // Inspect the indices.
946            let indices = &inner.last_index_per_reader;
947
948            assert_eq!(indices.get(&updates_subscriber1.token), Some(&0));
949            assert_eq!(indices.get(&updates_subscriber2_token), None); // token is unknown!
950        }
951
952        // When dropping the `LinkedChunk`, it closes the stream.
953        drop(linked_chunk);
954        assert_matches!(updates_subscriber1.as_mut().poll_next(&mut context1), Poll::Ready(None));
955    }
956
957    #[test]
958    fn test_update_into_items() {
959        let updates: Update<_, u32> =
960            Update::PushItems { at: Position::new(ChunkIdentifier(0), 0), items: vec![1, 2, 3] };
961
962        assert_eq!(updates.into_items(), vec![1, 2, 3]);
963
964        let updates: Update<u32, u32> = Update::Clear;
965        assert!(updates.into_items().is_empty());
966
967        let updates: Update<u32, u32> =
968            Update::RemoveItem { at: Position::new(ChunkIdentifier(0), 0) };
969        assert!(updates.into_items().is_empty());
970
971        let updates: Update<u32, u32> =
972            Update::ReplaceItem { at: Position::new(ChunkIdentifier(0), 0), item: 42 };
973        assert_eq!(updates.into_items(), vec![42]);
974    }
975}