662a0113608d87a6852cbffe162961d60849efd6
[folly.git] / folly / experimental / observer / Observer.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17
18 #include <folly/ThreadLocal.h>
19 #include <folly/experimental/observer/detail/Core.h>
20 #include <folly/experimental/observer/detail/Observer-pre.h>
21
22 namespace folly {
23 namespace observer {
24
25 /**
26  * Observer - a library which lets you create objects which track updates of
27  * their dependencies and get re-computed when any of the dependencies changes.
28  *
29  *
30  * Given an Observer, you can get a snapshot of the current version of the
31  * object it holds:
32  *
33  *   Observer<int> myObserver = ...;
34  *   Snapshot<int> mySnapshot = myObserver.getSnapshot();
35  * or simply
36  *   Snapshot<int> mySnapshot = *myObserver;
37  *
38  * Snapshot will hold a view of the object, even if object in the Observer
39  * gets updated.
40  *
41  *
42  * What makes Observer powerful is its ability to track updates to other
43  * Observers. Imagine we have two separate Observers A and B which hold
44  * integers.
45  *
46  *   Observer<int> observerA = ...;
47  *   Observer<int> observerB = ...;
48  *
49  * To compute a sum of A and B we can create a new Observer which would track
50  * updates to A and B and re-compute the sum only when necessary.
51  *
52  *   Observer<int> sumObserver = makeObserver(
53  *       [observerA, observerB] {
54  *         int a = **observerA;
55  *         int b = **observerB;
56  *         return a + b;
57  *       });
58  *
59  *   int sum = **sumObserver;
60  *
61  * Notice that a + b will be only called when either a or b is changed. Getting
62  * a snapshot from sumObserver won't trigger any re-computation.
63  *
64  *
65  * TLObserver is very similar to Observer, but it also keeps a thread-local
66  * cache for the observed object.
67  *
68  *   Observer<int> observer = ...;
69  *   TLObserver<int> tlObserver(observer);
70  *   auto& snapshot = *tlObserver;
71  *
72  *
73  * See ObserverCreator class if you want to wrap any existing subscription API
74  * in an Observer object.
75  */
76 template <typename T>
77 class Observer;
78
79 template <typename T>
80 class Snapshot {
81  public:
82   const T& operator*() const {
83     return *get();
84   }
85
86   const T* operator->() const {
87     return get();
88   }
89
90   const T* get() const {
91     return data_.get();
92   }
93
94   /**
95    * Return the version of the observed object.
96    */
97   size_t getVersion() const {
98     return version_;
99   }
100
101  private:
102   friend class Observer<T>;
103
104   Snapshot(
105       const observer_detail::Core& core,
106       std::shared_ptr<const T> data,
107       size_t version)
108       : data_(std::move(data)), version_(version), core_(&core) {
109     DCHECK(data_);
110   }
111
112   std::shared_ptr<const T> data_;
113   size_t version_;
114   const observer_detail::Core* core_;
115 };
116
117 template <typename T>
118 class Observer {
119  public:
120   explicit Observer(observer_detail::Core::Ptr core);
121
122   Snapshot<T> getSnapshot() const;
123   Snapshot<T> operator*() const {
124     return getSnapshot();
125   }
126
127   /**
128    * Check if we have a newer version of the observed object than the snapshot.
129    * Snapshot should have been originally from this Observer.
130    */
131   bool needRefresh(const Snapshot<T>& snapshot) const {
132     DCHECK_EQ(core_.get(), snapshot.core_);
133     return snapshot.getVersion() < core_->getVersionLastChange();
134   }
135
136  private:
137   observer_detail::Core::Ptr core_;
138 };
139
140 /**
141  * makeObserver(...) creates a new Observer<T> object given a functor to
142  * compute it. The functor can return T or std::shared_ptr<const T>.
143  *
144  * makeObserver(...) blocks until the initial version of Observer is computed.
145  * If creator functor fails (throws or returns a nullptr) during this first
146  * call, the exception is re-thrown by makeObserver(...).
147  *
148  * For all subsequent updates if creator functor fails (throws or returs a
149  * nullptr), the Observer (and all its dependents) is not updated.
150  */
151 template <typename F>
152 Observer<observer_detail::ResultOf<F>> makeObserver(F&& creator);
153
154 template <typename F>
155 Observer<observer_detail::ResultOfUnwrapSharedPtr<F>> makeObserver(F&& creator);
156
157 template <typename T>
158 class TLObserver {
159  public:
160   explicit TLObserver(Observer<T> observer);
161   TLObserver(const TLObserver<T>& other);
162
163   const Snapshot<T>& getSnapshotRef() const;
164   const Snapshot<T>& operator*() const {
165     return getSnapshotRef();
166   }
167
168  private:
169   Observer<T> observer_;
170   folly::ThreadLocal<Snapshot<T>> snapshot_;
171 };
172
173 /**
174  * Same as makeObserver(...), but creates TLObserver.
175  */
176 template <typename T>
177 TLObserver<T> makeTLObserver(Observer<T> observer) {
178   return TLObserver<T>(std::move(observer));
179 }
180
181 template <typename F>
182 auto makeTLObserver(F&& creator) {
183   return makeTLObserver(makeObserver(std::forward<F>(creator)));
184 }
185
186 template <typename T, bool CacheInThreadLocal>
187 struct ObserverTraits {};
188
189 template <typename T>
190 struct ObserverTraits<T, false> {
191   using type = Observer<T>;
192 };
193
194 template <typename T>
195 struct ObserverTraits<T, true> {
196   using type = TLObserver<T>;
197 };
198
199 template <typename T, bool CacheInThreadLocal>
200 using ObserverT = typename ObserverTraits<T, CacheInThreadLocal>::type;
201 }
202 }
203
204 #include <folly/experimental/observer/Observer-inl.h>