client-cpp  0.9.0
KaaObservable.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2014-2016 CyberVision, 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 
17 #ifndef KAA_OBSERVER_KAAOBSERVABLE_HPP_
18 #define KAA_OBSERVER_KAAOBSERVABLE_HPP_
19 
20 #include <functional>
21 #include <unordered_map>
22 #include <unordered_set>
23 #include <memory>
24 #include <utility>
25 
26 #include "kaa/KaaThread.hpp"
27 #include "kaa/logging/Log.hpp"
28 
29 namespace kaa {
30 
31 template<class Signature, class Key, class Function = std::function<Signature>>
33 {
34 public:
35  KaaObservable() : isNotifying_(false) { }
37 
38  bool addCallback(const Key& key, const Function& f)
39  {
40  if (isNotifying_) {
41  KAA_MUTEX_LOCKING("modificationGuard_");
42  KAA_MUTEX_UNIQUE_DECLARE(modificationGuardLock, modificationGuard_);
43  KAA_MUTEX_LOCKED("modificationGuard_");
44  auto it = slots_.find(key);
45  if (it != slots_.end() && !it->second.isRemoved()) {
46  return false;
47  }
48  slotsToRemove_.erase(key);
49  slotsToAdd_.insert(std::make_pair(key, CallbackWrapper(f)));
50  return true;
51  } else {
52  KAA_MUTEX_LOCKING("mainGuard_");
53  KAA_MUTEX_UNIQUE_DECLARE(mainGuardLock, mainGuard_);
54  KAA_MUTEX_LOCKED("mainGuard_");
55  return slots_.insert(std::make_pair(key, CallbackWrapper(f))).second;
56  }
57  }
58 
59  void removeCallback(const Key& key)
60  {
61  if (isNotifying_) {
62  KAA_MUTEX_LOCKING("modificationGuard_");
63  KAA_MUTEX_UNIQUE_DECLARE(modificationGuardLock, modificationGuard_);
64  KAA_MUTEX_LOCKED("modificationGuard_");
65  slotsToAdd_.erase(key);
66  slotsToRemove_.insert(key);
67 
68  auto it = slots_.find(key);
69  if (it != slots_.end()) {
70  it->second.remove();
71  }
72  } else {
73  KAA_MUTEX_LOCKING("mainGuard_");
74  KAA_MUTEX_UNIQUE_DECLARE(mainGuardLock, mainGuard_);
75  KAA_MUTEX_LOCKED("mainGuard_");
76  slots_.erase(key);
77  }
78  }
79 
80  template <typename... Args>
81  void operator()(Args&&... args)
82  {
83  isNotifying_ = true;
84 
85  KAA_MUTEX_LOCKING("mainGuard_");
86  KAA_MUTEX_UNIQUE_DECLARE(mainGuardLock, mainGuard_);
87  KAA_MUTEX_LOCKED("mainGuard_");
88 
89  for (auto& pair : slots_) {
90  try {
91  pair.second(std::forward<Args>(args)...);
92  } catch (...) {
93  }
94  }
95  isNotifying_ = false;
96 
97  KAA_MUTEX_LOCKING("modificationGuard_");
98  KAA_MUTEX_UNIQUE_DECLARE(modificationGuardLock, modificationGuard_);
99  KAA_MUTEX_LOCKED("modificationGuard_");
100 
101  for (auto it = slotsToAdd_.begin(); it != slotsToAdd_.end(); ++it) {
102  slots_[it->first] = it->second;
103  }
104  slotsToAdd_.clear();
105 
106  for (auto key : slotsToRemove_) {
107  slots_.erase(key);
108  }
109  slotsToRemove_.clear();
110  }
111 
112  bool isEmpty()
113  {
114  KAA_MUTEX_LOCKING("mainGuard_");
115  KAA_MUTEX_UNIQUE_DECLARE(mainGuardLock, mainGuard_);
116  KAA_MUTEX_LOCKED("mainGuard_");
117 
118  KAA_MUTEX_LOCKING("modificationGuard_");
119  KAA_MUTEX_UNIQUE_DECLARE(modificationGuardLock, modificationGuard_);
120  KAA_MUTEX_LOCKED("modificationGuard_");
121 
122  return slots_.empty() && slotsToAdd_.empty();
123  }
124 
125 private:
126  class CallbackWrapper
127  {
128  public:
129  CallbackWrapper() : isRemoved_(false) { }
130  CallbackWrapper(const Function& f) : callback_(f), isRemoved_(false) { }
131  CallbackWrapper(const CallbackWrapper& o) : callback_(o.callback_), isRemoved_((bool) o.isRemoved_) { }
132  CallbackWrapper(CallbackWrapper&& o) : callback_(std::move(o.callback_)), isRemoved_((bool) o.isRemoved_) { }
133  CallbackWrapper& operator=(const CallbackWrapper& o) { callback_ = o.callback_; isRemoved_ = (bool) o.isRemoved_; return *this; }
134  CallbackWrapper& operator=(CallbackWrapper&& o) { callback_ = std::move(o.callback_); isRemoved_ = (bool) o.isRemoved_; return *this; }
135 
136  template <typename... Args>
137  void operator()(Args&&... args)
138  {
139  if (!isRemoved_) {
140  callback_(std::forward<Args>(args)...);
141  }
142  }
143 
144  bool isRemoved() const { return isRemoved_; }
145  void remove() { isRemoved_ = true; }
146 
147  private:
148  Function callback_;
149  bool_type isRemoved_;
150  };
151 
152  std::unordered_map<Key, CallbackWrapper> slots_;
153  std::unordered_map<Key, CallbackWrapper> slotsToAdd_;
154  std::unordered_set<Key> slotsToRemove_;
155 
156  bool_type isNotifying_;
157 
158  KAA_MUTEX_DECLARE(mainGuard_);
159  KAA_MUTEX_DECLARE(modificationGuard_);
160 };
161 
162 }
163 
164 
165 #endif /* KAA_OBSERVER_KAAOBSERVABLE_HPP_ */
void operator()(Args &&...args)
std::atomic_bool bool_type
Definition: KaaThread.hpp:54
void removeCallback(const Key &key)
#define KAA_MUTEX_LOCKED(mutex_name)
Definition: Log.hpp:91
#define KAA_MUTEX_UNIQUE_DECLARE(name, mtx)
Definition: KaaThread.hpp:48
bool addCallback(const Key &key, const Function &f)
#define KAA_MUTEX_LOCKING(mutex_name)
Definition: Log.hpp:90