1/*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/private/SkIDChangeListener.h"
9
10/**
11 * Used to be notified when a gen/unique ID is invalidated, typically to preemptively purge
12 * associated items from a cache that are no longer reachable. The listener can
13 * be marked for deregistration if the cached item is remove before the listener is
14 * triggered. This prevents unbounded listener growth when cache items are routinely
15 * removed before the gen ID/unique ID is invalidated.
16 */
17
18SkIDChangeListener::SkIDChangeListener() : fShouldDeregister(false) {}
19
20SkIDChangeListener::~SkIDChangeListener() = default;
21
22using List = SkIDChangeListener::List;
23
24List::List() = default;
25
26List::~List() {
27 // We don't need the mutex. No other thread should have this list while it's being
28 // destroyed.
29 for (int i = 0; i < fListeners.count(); ++i) {
30 if (!fListeners[i]->shouldDeregister()) {
31 fListeners[i]->changed();
32 }
33 fListeners[i]->unref();
34 }
35}
36
37void List::add(sk_sp<SkIDChangeListener> listener, bool singleThreaded) {
38 if (!listener) {
39 return;
40 }
41 SkASSERT(!listener->shouldDeregister());
42
43 auto add = [&] {
44 // Clean out any stale listeners before we append the new one.
45 for (int i = 0; i < fListeners.count(); ++i) {
46 if (fListeners[i]->shouldDeregister()) {
47 fListeners[i]->unref();
48 fListeners.removeShuffle(i--); // No need to preserve the order after i.
49 }
50 }
51 *fListeners.append() = listener.release();
52 };
53
54 if (singleThreaded) {
55 add();
56 } else {
57 SkAutoMutexExclusive lock(fMutex);
58 add();
59 }
60}
61
62int List::count() {
63 SkAutoMutexExclusive lock(fMutex);
64 return fListeners.count();
65}
66
67void List::changed(bool singleThreaded) {
68 auto visit = [this]() {
69 for (SkIDChangeListener* listener : fListeners) {
70 if (!listener->shouldDeregister()) {
71 listener->changed();
72 }
73 // Listeners get at most one shot, so whether these triggered or not, blow them away.
74 listener->unref();
75 }
76 fListeners.reset();
77 };
78
79 if (singleThreaded) {
80 visit();
81 } else {
82 SkAutoMutexExclusive lock(fMutex);
83 visit();
84 }
85}
86
87void List::reset(bool singleThreaded) {
88 if (singleThreaded) {
89 fListeners.unrefAll();
90 } else {
91 SkAutoMutexExclusive lock(fMutex);
92 fListeners.unrefAll();
93 }
94}
95