Overte C++ Documentation
Transaction.h
1 //
2 // Transaction.h
3 // libraries/workload/src/workload
4 //
5 // Created by Sam Gateau 2018.03.12
6 // Copyright 2018 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_workload_Transaction_h
13 #define hifi_workload_Transaction_h
14 
15 #include <atomic>
16 #include <mutex>
17 #include <memory>
18 #include <vector>
19 #include <glm/glm.hpp>
20 
21 #include "Proxy.h"
22 
23 
24 namespace workload {
25 
26  namespace indexed_container {
27 
28  using Index = int32_t;
29  const Index MAXIMUM_INDEX{ 1 << 30 };
30  const Index INVALID_INDEX{ -1 };
31  using Indices = std::vector< Index >;
32 
33  template <Index MaxNumElements = MAXIMUM_INDEX>
34  class Allocator {
35  public:
36  Allocator() {}
37  Indices _freeIndices;
38  Index _nextNewIndex{ 0 };
39 
40  bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); }
41  Index getNumLiveIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); }
42  Index getNumFreeIndices() const { return (Index)_freeIndices.size(); }
43  Index getNumAllocatedIndices() const { return _nextNewIndex; }
44 
45  Index allocateIndex() {
46  if (_freeIndices.empty()) {
47  Index index = _nextNewIndex;
48  if (index >= MaxNumElements) {
49  // abort! we are trying to go overboard with the total number of allocated elements
50  assert(false);
51  // This should never happen because Bricks are allocated along with the cells and there
52  // is already a cap on the cells allocation
53  return INVALID_INDEX;
54  }
55  _nextNewIndex++;
56  return index;
57  } else {
58  // TODO: sort _freeIndices when neccessary to help keep used allocated indices more tightly packed
59  Index index = _freeIndices.back();
60  _freeIndices.pop_back();
61  return index;
62  }
63  }
64 
65  void freeIndex(Index index) {
66  if (checkIndex(index)) {
67  _freeIndices.push_back(index);
68  }
69  }
70 
71  void clear() {
72  _freeIndices.clear();
73  _nextNewIndex = 0;
74  }
75  };
76  }
77 
78 
79  using Index = indexed_container::Index;
80  using IndexVector = indexed_container::Indices;
81 
82  using ProxyID = Index;
83  const ProxyID INVALID_PROXY_ID{ indexed_container ::INVALID_INDEX };
84 
85 // Transaction is the mechanism to make any change to the Space.
86 // Whenever a new proxy need to be reset,
87 // or when an proxy changes its position or its size
88 // or when an proxy is destroyed
89 // These changes must be expressed through the corresponding command from the Transaction
90 // The Transaction is then queued on the Space so all the pending transactions can be consolidated and processed at the time
91 // of updating the space at the Frame boundary.
92 //
93 class Transaction {
94  friend class Space;
95 public:
96  using ProxyPayload = Sphere;
97 
98  using Reset = std::tuple<ProxyID, ProxyPayload, Owner>;
99  using Remove = ProxyID;
100  using Update = std::tuple<ProxyID, ProxyPayload>;
101 
102  using Resets = std::vector<Reset>;
103  using Removes = std::vector<Remove>;
104  using Updates = std::vector<Update>;
105 
106  Transaction() {}
107  ~Transaction() {}
108 
109  // Proxy transactions
110  void reset(ProxyID id, const ProxyPayload& sphere, const Owner& owner);
111  void reset(const Resets& resets);
112  void remove(ProxyID id);
113  void remove(const Removes& removes);
114  bool hasRemovals() const { return !_removedItems.empty(); }
115 
116  void update(ProxyID id, const ProxyPayload& sphere);
117  void update(const Updates& updates);
118 
119  void reserve(const std::vector<Transaction>& transactionContainer);
120  void merge(const std::vector<Transaction>& transactionContainer);
121  void merge(std::vector<Transaction>&& transactionContainer);
122  void merge(const Transaction& transaction);
123  void merge(Transaction&& transaction);
124  void clear();
125 
126 protected:
127 
128 
129  Resets _resetItems;
130  Removes _removedItems;
131  Updates _updatedItems;
132 };
133 typedef std::vector<Transaction> TransactionQueue;
134 
135 class Collection {
136 public:
137  Collection();
138  virtual ~Collection();
139 
140  virtual void clear();
141 
142  // This call is thread safe, can be called from anywhere to allocate a new ID
143  ProxyID allocateID();
144 
145  // Check that the ID is valid and allocated for this collection, this a threadsafe call
146  bool isAllocatedID(const ProxyID& id) const;
147 
148  // THis is the total number of allocated proxies, this a threadsafe call
149  Index getNumAllocatedProxies() const { return _IDAllocator.getNumAllocatedIndices(); }
150 
151  // Enqueue transaction to the collection
152  void enqueueTransaction(const Transaction& transaction);
153 
154  // Enqueue transaction to the collection
155  void enqueueTransaction(Transaction&& transaction);
156 
157  // Enqueue end of frame transactions boundary
158  uint32_t enqueueFrame();
159 
160  // Process the pending transactions queued
161  virtual void processTransactionQueue();
162 
163 protected:
164 
165  // Thread safe elements that can be accessed from anywhere
166  indexed_container::Allocator<> _IDAllocator;
167 
168  std::mutex _transactionQueueMutex;
169  TransactionQueue _transactionQueue;
170 
171 
172  std::mutex _transactionFramesMutex;
173  using TransactionFrames = std::vector<Transaction>;
174  TransactionFrames _transactionFrames;
175  uint32_t _transactionFrameNumber{ 0 };
176 
177  // Process one transaction frame
178  virtual void processTransactionFrame(const Transaction& transaction) = 0;
179 };
180 
181 } // namespace workload
182 
183 #endif // hifi_workload_Transaction_h