PDI 0.4.1

Data exchange made easy

ref_any.h
1 /*******************************************************************************
2  * Copyright (C) 2015-2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of CEA nor the names of its contributors may be used to
13  * endorse or promote products derived from this software without specific
14  * prior written permission.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  ******************************************************************************/
24 
25 #ifndef PDI_REF_ANY_H_
26 #define PDI_REF_ANY_H_
27 
28 #include <cassert>
29 #include <functional>
30 #include <memory>
31 #include <new>
32 #include <unordered_map>
33 
34 #include <pdi/pdi_fwd.h>
35 #include <pdi/datatype.h>
36 #include <pdi/error.h>
37 
38 
39 namespace PDI {
40 
41 namespace {
42 
43 template<bool R, bool W>
44 struct Ref_access {
45  typedef void type;
46 };
47 
48 template<bool R>
49 struct Ref_access<R, true> {
50  typedef void* type;
51 };
52 
53 template<>
54 struct Ref_access<true, false> {
55  typedef const void* type;
56 };
57 
58 } //namespace <anonymous>
59 
60 
64 class PDI_EXPORT Ref_base
65 {
66 protected:
69  struct PDI_NO_EXPORT Referenced {
71  void* m_buffer;
72 
74  std::function<void(void*)> m_delete;
75 
78 
80  int m_owners;
81 
84 
87 
89  std::unordered_map<const Ref_base*, std::function<void(Ref)> > m_notifications;
90 
91 
92  Referenced() = delete;
93 
94  Referenced(const Referenced&) = delete;
95 
96  Referenced(Referenced&&) = delete;
97 
105  Referenced(void* buffer, std::function<void(void*)> deleter, Datatype_uptr type, bool readable, bool writable) noexcept:
106  m_buffer{buffer},
107  m_delete{std::move(deleter)},
108  m_type{std::move(type)},
109  m_owners{0},
110  m_read_locks{readable ? 0 : 1},
111  m_write_locks{writable ? 0 : 1}
112  {
113  assert(buffer);
114  }
115 
117  {
118  if (m_buffer) {
119  m_type->destroy_data(m_buffer);
120  m_delete(m_buffer);
121  }
122  assert(!m_owners);
123  assert(m_read_locks == 0 || m_read_locks == 1);
124  assert(m_write_locks == 0 || m_write_locks == 1);
125  assert(m_notifications.empty());
126  }
127 
128  }; // class Referenced
129 
130 
134 
135 
138  static Referenced PDI_NO_EXPORT* get_content(const Ref_base& other) noexcept
139  {
140  if ( !other.m_content ) return NULL;
141  if ( !other.m_content->m_buffer ) return NULL;
142  return other.m_content;
143  }
144 
145  // Symbol should not be exported, but it required to force
146  // generation of all 4 variants of `Ref_any::copy`
147  static Ref do_copy(Ref_r ref);
148 
151  Ref_base() noexcept:
152  m_content(nullptr)
153  {}
154 
155  Ref_base(const Ref_base&) = delete;
156 
157  Ref_base(Ref_base&&) = delete;
158 
159  Ref_base& operator = (const Ref_base&) = delete;
160 
161  Ref_base& operator = (Ref_base&&) = delete;
162 
163 public:
166  const Datatype& type() const noexcept;
167 
168  size_t hash() const noexcept
169  {
170  return std::hash<Referenced*>()(get_content(*this));
171  }
172 
173 }; // class Data_ref_base
174 
175 
189 template<bool R, bool W>
190 class PDI_EXPORT Ref_any:
191  public Ref_base
192 {
193 public:
196  Ref_any() = default;
197 
204  Ref_any(const Ref_any& other) noexcept:
205  Ref_base()
206  {
207  link(get_content(other));
208  }
209 
216  template<bool OR, bool OW>
217  Ref_any(const Ref_any<OR, OW>& other) noexcept:
218  Ref_base()
219  {
220  link(get_content(other));
221  }
222 
226  Ref_any(Ref_any&& other) noexcept:
227  Ref_base()
228  {
229  if (!other.m_content || !other.m_content->m_buffer) return;
230  // the other ref notification disappears
231  other.m_content->m_notifications.erase(&other);
232  // since we get the same privileges as those we release we can just steal the content
233  m_content = other.m_content;
234  other.m_content = nullptr;
235  }
236 
245  Ref_any(void* data, std::function<void(void*)> freefunc, Datatype_uptr type, bool readable, bool writable):
246  Ref_base()
247  {
248  if (type->datasize() && !data && (readable||writable)) {
249  throw Error{PDI_ERR_TYPE, "Referencing null data with non-null size"};
250  }
251  if (data) link(new Referenced(data, freefunc, std::move(type), readable, writable));
252  }
253 
257  {
258  reset();
259  }
260 
261  bool operator== (const Ref_base& o) const noexcept
262  {
263  is_null();
264  return m_content == get_content(o);
265  }
266 
267  bool operator!= (const Ref_base& o) const noexcept
268  {
269  is_null();
270  return m_content != get_content(o);
271  }
272 
273  bool operator< (const Ref_base& o) const noexcept
274  {
275  is_null();
276  return m_content < get_content(o);
277  }
278 
279  bool operator> (const Ref_base& o) const noexcept
280  {
281  is_null();
282  return m_content > get_content(o);
283  }
284 
285  bool operator<= (const Ref_base& o) const noexcept
286  {
287  is_null();
288  return m_content <= get_content(o);
289  }
290 
291  bool operator>= (const Ref_base& o) const noexcept
292  {
293  is_null();
294  return m_content >= get_content(o);
295  }
296 
301  operator typename Ref_access<R, W>::type() const
302  {
303  return get();
304  }
305 
310  typename Ref_access<R, W>::type get() const
311  {
312  if (is_null()) throw Error{PDI_ERR_RIGHT, "Trying to dereference a null reference"};
313  return m_content->m_buffer;
314  }
315 
320  typename Ref_access<R, W>::type get(std::nothrow_t) const noexcept
321  {
322  if (is_null()) return nullptr;
323  return m_content->m_buffer;
324  }
325 
330  operator bool () const noexcept
331  {
332  return !is_null();
333  }
334 
337  void reset() noexcept
338  {
339  if (m_content) unlink();
340  }
341 
347  Ref copy() const
348  {
349  return do_copy(*this);
350  }
351 
358  void* release() noexcept
359  {
360  if (is_null()) return nullptr;
361 
362  // notify everybody of the nullification
363  while (!m_content->m_notifications.empty()) {
364  // get the key of a notification
365  const Ref_base* key = m_content->m_notifications.begin()->first;
366  // call this notification, this might invalidate any iterator
367  m_content->m_notifications.begin()->second(*this);
368  // remove the notification we just called
369  m_content->m_notifications.erase(key);
370  }
371 
372  void* result = m_content->m_buffer;
373  m_content->m_buffer = nullptr;
374 
375  unlink();
376 
377  return result;
378  }
379 
384  void on_nullify(std::function<void(Ref)> notifier) const noexcept
385  {
386  if (!is_null()) m_content->m_notifications[this] = notifier;
387  }
388 
389 private:
397  bool PDI_NO_EXPORT is_null() const noexcept
398  {
399  if (!m_content) return true;
400  if (!m_content->m_buffer) {
401  unlink();
402  return true;
403  }
404  return false;
405  }
406 
411  void PDI_NO_EXPORT unlink() const noexcept
412  {
413  assert(m_content);
414  m_content->m_notifications.erase(this);
415  if (R || W) --m_content->m_write_locks;
416  if (W) --m_content->m_read_locks;
417  --m_content->m_owners;
418  if (!m_content->m_owners) delete m_content;
419  m_content = nullptr;
420  }
421 
429  void PDI_NO_EXPORT link(Referenced* content) noexcept
430  {
431  assert(!m_content);
432  if (!content || !content->m_buffer) return; // null ref
433  if (R && content->m_read_locks) return;
434  if (W && content->m_write_locks) return;
435  m_content = content;
436  ++m_content->m_owners;
437  if (R || W) ++m_content->m_write_locks;
438  if (W) ++m_content->m_read_locks;
439  }
440 
441 };
442 
443 } // namespace PDI
444 
445 namespace std {
446 
447 template<bool R, bool W>
448 struct hash<PDI::Ref_any<R,W>> {
449  size_t operator() (const PDI::Ref_any<R,W>& r) const noexcept
450  {
451  return r.hash();
452  }
453 };
454 
455 } // namespace std
456 
457 #endif // PDI_REF_ANY_H_
Referenced * m_content
Pointer on the data content, can be null if the ref is null.
Definition: ref_any.h:133
~Referenced()
Definition: ref_any.h:116
void * m_buffer
buffer that contains data
Definition: ref_any.h:71
int m_owners
number of references to this content
Definition: ref_any.h:80
int m_write_locks
number of locks preventing write access (should always remain between 0 & 2 inclusive w...
Definition: ref_any.h:86
std::function< void(void *)> m_delete
The function to call to deallocate the buffer memory.
Definition: ref_any.h:74
STL namespace.
A Datatype is a Datatype_template that accepts no argument.
Definition: datatype.h:40
static Referenced * get_content(const Ref_base &other) noexcept
Function to access the content from a reference with different access right.
Definition: ref_any.h:138
Invalid type error.
Definition: pdi.h:99
Ref_any(const Ref_any &other) noexcept
Copies an existing reference.
Definition: ref_any.h:204
A dynamically typed reference to data with automatic memory management and read/write locking semanti...
Definition: pdi_fwd.h:77
std::unique_ptr< Datatype > Datatype_uptr
Definition: pdi_fwd.h:62
std::unordered_map< const Ref_base *, std::function< void(Ref)> > m_notifications
Nullification notifications registered on this instance.
Definition: ref_any.h:89
Datatype_uptr m_type
type of the data inside the buffer
Definition: ref_any.h:77
Ref_base() noexcept
Constructs a null reference.
Definition: ref_any.h:151
void * release() noexcept
Releases ownership of the referenced raw data by nullifying all existing references.
Definition: ref_any.h:358
A conflict of onwership over a content has been raised.
Definition: pdi.h:97
int m_read_locks
number of locks preventing read access
Definition: ref_any.h:83
~Ref_any()
Destructor.
Definition: ref_any.h:256
A common base for all references, whatever their access privileges in order to ensure they share the ...
Definition: ref_any.h:64
void reset() noexcept
Nullify the reference.
Definition: ref_any.h:337
Ref_any(void *data, std::function< void(void *)> freefunc, Datatype_uptr type, bool readable, bool writable)
Creates a reference to currently unreferenced data.
Definition: ref_any.h:245
Definition: array_datatype.h:31
void on_nullify(std::function< void(Ref)> notifier) const noexcept
Registers a nullification callback.
Definition: ref_any.h:384
Ref_any(const Ref_any< OR, OW > &other) noexcept
Copies an existing reference with different privileges.
Definition: ref_any.h:217
Ref_any(Ref_any &&other) noexcept
Moves an existing reference.
Definition: ref_any.h:226
Definition: error.h:37
Referenced(void *buffer, std::function< void(void *)> deleter, Datatype_uptr type, bool readable, bool writable) noexcept
Constructs the content.
Definition: ref_any.h:105
Manipulate and grant access to a buffer depending on the remaining right access (read/write).
Definition: ref_any.h:69
Ref copy() const
Makes a copy of the raw content behind this reference and returns a new reference.
Definition: ref_any.h:347
size_t hash() const noexcept
Definition: ref_any.h:168