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}