TensorFlow Serving C++ API Documentation
observer.h
1 /* Copyright 2016 Google Inc. All Rights Reserved.
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 ==============================================================================*/
15 
16 #ifndef TENSORFLOW_SERVING_UTIL_OBSERVER_H_
17 #define TENSORFLOW_SERVING_UTIL_OBSERVER_H_
18 
19 #include <algorithm>
20 #include <functional>
21 #include <memory>
22 #include <vector>
23 
24 #include "tensorflow/core/platform/logging.h"
25 #include "tensorflow/core/platform/macros.h"
26 #include "tensorflow/core/platform/mutex.h"
27 #include "tensorflow/core/platform/thread_annotations.h"
28 #include "tensorflow/core/platform/types.h"
29 
30 namespace tensorflow {
31 namespace serving {
32 
33 // An observer calls a std::function whenever a specific event happens. The
34 // difference between an observer and a plain std::function is that it is safe
35 // to destroy an observer even while external code may be notifying it.
36 //
37 // Example use:
38 // void ObserveSomeNotifications() {
39 // mutex mu;
40 // int num_notifications = 0;
41 // Observer<> observer([&](){ mutex_lock l(mu); ++num_notifications; });
42 // AsynchronouslySendNotificationsForAWhile(observer.Notifier());
43 // SleepForALittleWhile();
44 // {
45 // mutex_lock l(mu);
46 // LOG(INFO) << "Currently " << num_notifications << " notifications";
47 // }
48 // // Let 'observer' fall out of scope and be deleted. Any future
49 // // notifications will be no-ops.
50 // }
51 //
52 // Note that the current implementation serializes the notification calls, so it
53 // should not be used with long notifications if the inability to overlap them
54 // would be critical for performance.
55 //
56 // Note that naive use of this class will result in "orphaned", no-op instances
57 // std::function laying around. If this is a concern please use ObserverList to
58 // manage large collections of observers.
59 template <typename... Args>
60 class Observer {
61  public:
62  // The type of function that this observer will wrap.
63  using Function = std::function<void(Args...)>;
64 
65  // Wraps 'f' as an observer.
66  explicit Observer(Function f) : impl_(std::make_shared<Impl>(std::move(f))) {}
67 
68  // Destruction will cause all notifiers to become no-ops.
69  ~Observer() {
70  if (impl_ != nullptr) {
71  impl_->Orphan();
72  }
73  }
74 
75  // Returns a function that will notify this observer for its lifetime.
76  // Becomes a no-op after the observer is destroyed.
77  Function Notifier() const {
78  auto impl = impl_;
79  DCHECK(impl != nullptr); // An implementation invariant.
80  return [impl](Args... args) { impl->Notify(std::forward<Args>(args)...); };
81  }
82 
83  private:
84  template <typename... T>
85  friend class ObserverList;
86 
87  // The underlying implementation.
88  class Impl;
89 
90  // The implementation, shared with this object and all notifiers.
91  std::shared_ptr<Impl> impl_;
92 
93  TF_DISALLOW_COPY_AND_ASSIGN(Observer);
94 };
95 
96 // An observer list is essentially a std::vector<Observer>, the key difference
97 // is that an ObserverList will garbage collect orphaned notifiers.
98 template <typename... Args>
99 class ObserverList {
100  public:
101  // Add an observer to the list.
102  void Add(const Observer<Args...>& new_observer) {
103  // Try to reuse an existing slot if possible.
104  for (auto& observer : observers_) {
105  if (observer->IsOrphaned()) {
106  observer = new_observer.impl_;
107  return;
108  }
109  }
110  observers_.push_back(new_observer.impl_);
111  }
112 
113  // Notify all observers in the list.
114  void Notify(Args... args) {
115  for (const auto& observer : observers_) {
116  observer->Notify(std::forward<Args>(args)...);
117  }
118  }
119 
120  // Clear all observers from this list.
121  void Clear() { observers_.clear(); }
122 
123  private:
124  // The impls of all observers added to this list.
125  std::vector<std::shared_ptr<typename Observer<Args...>::Impl>> observers_;
126 };
127 
129 // Implementation details follow. API users need not read.
130 
131 template <typename... Args>
132 class Observer<Args...>::Impl {
133  public:
134  explicit Impl(Function f) : f_(std::move(f)) {}
135 
136  bool IsOrphaned() const {
137  mutex_lock lock(mutex_);
138  return f_ == nullptr;
139  }
140 
141  void Orphan() {
142  mutex_lock lock(mutex_);
143  f_ = nullptr;
144  }
145 
146  void Notify(Args... args) const {
147  mutex_lock lock(mutex_);
148  if (f_ != nullptr) {
149  f_(std::forward<Args>(args)...);
150  }
151  }
152 
153  private:
154  mutable mutex mutex_;
155  // The function to call when an observed even occurs.
156  Function f_ TF_GUARDED_BY(mutex_);
157 };
158 
159 } // namespace serving
160 } // namespace tensorflow
161 
162 #endif // TENSORFLOW_SERVING_UTIL_OBSERVER_H_