Adds writer test case for RCU
[folly.git] / folly / experimental / observer / Observer.h
1 /*
2  * Copyright 2016-present 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   template <typename Observable, typename Traits>
138   friend class ObserverCreator;
139
140   observer_detail::Core::Ptr core_;
141 };
142
143 /**
144  * makeObserver(...) creates a new Observer<T> object given a functor to
145  * compute it. The functor can return T or std::shared_ptr<const T>.
146  *
147  * makeObserver(...) blocks until the initial version of Observer is computed.
148  * If creator functor fails (throws or returns a nullptr) during this first
149  * call, the exception is re-thrown by makeObserver(...).
150  *
151  * For all subsequent updates if creator functor fails (throws or returs a
152  * nullptr), the Observer (and all its dependents) is not updated.
153  */
154 template <typename F>
155 Observer<observer_detail::ResultOf<F>> makeObserver(F&& creator);
156
157 template <typename F>
158 Observer<observer_detail::ResultOfUnwrapSharedPtr<F>> makeObserver(F&& creator);
159
160 template <typename T>
161 class TLObserver {
162  public:
163   explicit TLObserver(Observer<T> observer);
164   TLObserver(const TLObserver<T>& other);
165
166   const Snapshot<T>& getSnapshotRef() const;
167   const Snapshot<T>& operator*() const {
168     return getSnapshotRef();
169   }
170
171  private:
172   Observer<T> observer_;
173   folly::ThreadLocal<Snapshot<T>> snapshot_;
174 };
175
176 /**
177  * Same as makeObserver(...), but creates TLObserver.
178  */
179 template <typename T>
180 TLObserver<T> makeTLObserver(Observer<T> observer) {
181   return TLObserver<T>(std::move(observer));
182 }
183
184 template <typename F>
185 auto makeTLObserver(F&& creator) {
186   return makeTLObserver(makeObserver(std::forward<F>(creator)));
187 }
188
189 template <typename T, bool CacheInThreadLocal>
190 struct ObserverTraits {};
191
192 template <typename T>
193 struct ObserverTraits<T, false> {
194   using type = Observer<T>;
195 };
196
197 template <typename T>
198 struct ObserverTraits<T, true> {
199   using type = TLObserver<T>;
200 };
201
202 template <typename T, bool CacheInThreadLocal>
203 using ObserverT = typename ObserverTraits<T, CacheInThreadLocal>::type;
204 } // namespace observer
205 } // namespace folly
206
207 #include <folly/experimental/observer/Observer-inl.h>