Lumiera  0.pre.03
»edit your freedom«
buffer-metadata.hpp
Go to the documentation of this file.
1 /*
2  BUFFER-METADATA.hpp - internal metadata for data buffer providers
3 
4  Copyright (C) Lumiera.org
5  2011, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
53 #ifndef STEAM_ENGINE_BUFFR_METADATA_H
54 #define STEAM_ENGINE_BUFFR_METADATA_H
55 
56 
57 #include "lib/error.hpp"
58 #include "lib/symbol.hpp"
59 #include "lib/hash-value.h"
60 #include "lib/util-foreach.hpp"
61 #include "include/logging.h"
64 #include "lib/nocopy.hpp"
65 
66 #include <unordered_map>
67 
68 
69 namespace steam {
70 namespace engine {
71 
72  using lib::HashVal;
73  using lib::Literal;
74  using util::for_each;
75 
76  namespace error = lumiera::error;
77 
78  namespace metadata {
79  class Key;
80  class Entry;
81  }
82  class BufferMetadata;
83 
84 
85 
86 
93  { NIL,
94  FREE,
98  };
99 
100 
101 
102 
103 
104 
105  namespace { // internal constants to mark the default case
106 
107  const LocalKey UNSPECIFIC;
108  const TypeHandler RAW_BUFFER;
109 
110  inline bool
111  nontrivial (TypeHandler const& toVerify)
112  {
113  return RAW_BUFFER != toVerify;
114  }
115 
116  inline bool
117  nontrivial (LocalKey const& toVerify)
118  {
119  return UNSPECIFIC != toVerify;
120  }
121  }
122 
123 
124 
125  /* === Metadata Implementation === */
126 
127  namespace metadata {
128 
129  namespace { // details of hash calculation
130  template<typename VAL>
131  HashVal
132  chainedHash(HashVal accumulatedHash, VAL changedValue)
133  {
134  boost::hash_combine (accumulatedHash, changedValue);
135  return accumulatedHash;
136  }
137  }
138 
139 
146  class Key
147  {
148  HashVal parent_;
149  HashVal hashID_;
150 
151  protected:
152  size_t storageSize_;
153  TypeHandler instanceFunc_;
154  LocalKey specifics_;
155 
156 
157  public:
164  Key (HashVal familyID, size_t storageSize)
165  : parent_(familyID)
166  , hashID_(chainedHash (familyID, storageSize))
167  , storageSize_(storageSize)
168  , instanceFunc_(RAW_BUFFER)
169  , specifics_(UNSPECIFIC)
170  { }
171 
172  // standard copy operations permitted
173 
178  Key (Key const& parent, size_t differingStorageSize)
179  : parent_(parent.hashID_)
180  , hashID_(chainedHash (parent_, differingStorageSize))
181  , storageSize_(differingStorageSize) // differing from parent
182  , instanceFunc_(parent.instanceFunc_)
183  , specifics_(parent.specifics_)
184  { }
185 
186 
191  Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions)
192  : parent_(parent.hashID_)
193  , hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
194  , storageSize_(parent.storageSize_)
195  , instanceFunc_(differingTypeHandlerFunctions) // differing from parent
196  , specifics_(parent.specifics_)
197  { }
198 
199 
204  Key (Key const& parent, LocalKey anotherTypeSpecificInternalID)
205  : parent_(parent.hashID_)
206  , hashID_(chainedHash (parent_, anotherTypeSpecificInternalID))
207  , storageSize_(parent.storageSize_)
208  , instanceFunc_(parent.instanceFunc_)
209  , specifics_(anotherTypeSpecificInternalID) // differing from parent
210  { }
211 
212 
219  static Key
220  forEntry (Key const& parent, const void* bufferAddr, LocalKey const& implID =UNSPECIFIC)
221  {
222  Key newKey(parent);
223  if (bufferAddr)
224  {
225  newKey.parent_ = HashVal(parent);
226  newKey.hashID_ = chainedHash(parent, bufferAddr);
227  if (nontrivial(implID))
228  {
229  REQUIRE (!newKey.specifics_.isDefined(),
230  "Implementation defined local key should not be overridden. "
231  "Underlying buffer type already defines a nontrivial LocalKey");
232  newKey.specifics_ = implID;
233  } }
234  return newKey;
235  }
236 
237  void
238  useTypeHandlerFrom (Key const& ref)
239  {
240  if (nontrivial(this->instanceFunc_))
241  throw error::Logic ("unable to supersede an already attached TypeHandler"
242  , LERR_(LIFECYCLE));
243  instanceFunc_ = ref.instanceFunc_;
244  }
245 
246 
247  LocalKey const& localKey() const { return specifics_;}
248  size_t storageSize() const { return storageSize_; }
249 
250  HashVal parentKey() const { return parent_;}
251  operator HashVal() const { return hashID_;}
252  };
253 
254 
269  class Entry
270  : public Key
271  {
272  BufferState state_;
273  void* buffer_;
274 
275  protected:
276  Entry (Key const& parent, void* bufferPtr =0, LocalKey const& implID =UNSPECIFIC)
277  : Key (Key::forEntry (parent, bufferPtr, implID))
278  , state_(bufferPtr? LOCKED:NIL)
279  , buffer_(bufferPtr)
280  { }
281 
284 
285  // standard copy operations permitted
286 
287  public:
290  bool
291  isLocked() const
292  {
293  ASSERT (!buffer_ || (NIL != state_ && FREE != state_));
294  return bool(buffer_);
295  }
296 
300  bool
301  isTypeKey() const
302  {
303  return NIL == state_ && !buffer_;
304  }
305 
306 
308  state() const
309  {
310  return state_;
311  }
312 
313  void*
314  access()
315  {
316  __must_not_be_NIL();
317  __must_not_be_FREE();
318 
319  ENSURE (buffer_);
320  return buffer_;
321  }
322 
324  Entry&
325  mark (BufferState newState)
326  {
327  __must_not_be_NIL();
328 
329  if ( (state_ == FREE && newState == LOCKED)
330  ||(state_ == LOCKED && newState == EMITTED)
331  ||(state_ == LOCKED && newState == BLOCKED)
332  ||(state_ == LOCKED && newState == FREE)
333  ||(state_ == EMITTED && newState == BLOCKED)
334  ||(state_ == EMITTED && newState == FREE)
335  ||(state_ == BLOCKED && newState == FREE))
336  {
337  // allowed transition
338  if (newState == FREE)
339  invokeEmbeddedDtor_and_clear();
340  if (newState == LOCKED)
341  invokeEmbeddedCtor();
342  state_ = newState;
343  return *this;
344  }
345 
346  throw error::Fatal ("Invalid buffer state transition.");
347  }
348 
349  Entry&
350  lock (void* newBuffer)
351  {
352  __must_be_FREE();
353  buffer_ = newBuffer;
354  return mark (LOCKED);
355  }
356 
357  Entry&
358  invalidate (bool invokeDtor =true)
359  {
360  if (buffer_ && invokeDtor)
361  invokeEmbeddedDtor_and_clear();
362  buffer_ = 0;
363  state_ = FREE;
364  return *this;
365  }
366 
367 
368  protected:
372  void
374  {
375  __buffer_required();
376  if (nontrivial (instanceFunc_))
377  instanceFunc_.createAttached (buffer_);
378  }
379 
383  void
385  {
386  __buffer_required();
387  if (nontrivial (instanceFunc_))
388  instanceFunc_.destroyAttached (buffer_);
389  buffer_ = 0;
390  }
391 
392  private:
393  void
394  __must_not_be_NIL() const
395  {
396  if (NIL == state_)
397  throw error::Fatal ("Buffer metadata entry with state==NIL encountered."
398  "State transition logic broken (programming error)"
399  , LERR_(LIFECYCLE));
400  }
401 
402  void
403  __must_not_be_FREE() const
404  {
405  if (FREE == state_)
406  throw error::Logic ("Buffer is inaccessible (marked as free). "
407  "Need a new buffer pointer in order to lock an entry. "
408  "You should invoke markLocked(buffer) prior to access."
409  , LERR_(LIFECYCLE));
410  }
411 
412  void
413  __must_be_FREE() const
414  {
415  if (FREE != state_)
416  throw error::Logic ("Buffer already in use"
417  , LERR_(LIFECYCLE));
418  REQUIRE (!buffer_, "Buffer marked as free, "
419  "but buffer pointer is set.");
420  }
421 
422  void
423  __buffer_required() const
424  {
425  if (!buffer_)
426  throw error::Fatal ("Need concrete buffer for any further operations");
427  }
428  };
429 
430 
431 
441  class Table
442  {
443  typedef std::unordered_map<HashVal,Entry> MetadataStore;
444 
445  MetadataStore entries_;
446 
447  public:
448  ~Table() { verify_all_buffers_freed(); }
449 
454  Entry*
455  fetch (HashVal hashID)
456  {
457  MetadataStore::iterator pos = entries_.find (hashID);
458  if (pos != entries_.end())
459  return &(pos->second);
460  else
461  return NULL;
462  }
463 
464  const Entry*
465  fetch (HashVal hashID) const
466  {
467  MetadataStore::const_iterator pos = entries_.find (hashID);
468  if (pos != entries_.end())
469  return &(pos->second);
470  else
471  return NULL;
472  }
473 
482  Entry&
483  store (Entry const& newEntry)
484  {
485  using std::make_pair;
486  REQUIRE (!fetch (newEntry), "duplicate buffer metadata entry");
487  MetadataStore::iterator pos = entries_.insert (make_pair (HashVal(newEntry), newEntry))
488  .first;
489 
490  ENSURE (pos != entries_.end());
491  return pos->second;
492  }
493 
494  void
495  remove (HashVal hashID)
496  {
497  uint cnt = entries_.erase (hashID);
498  ENSURE (cnt, "entry to remove didn't exist");
499  }
500 
501  private:
502  void
503  verify_all_buffers_freed()
504  try
505  {
506  for_each (entries_, verify_is_free);
507  }
508  ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store")
509 
510  static void
511  verify_is_free (std::pair<HashVal, Entry> const& e)
512  {
513  WARN_IF (e.second.isLocked(), engine,
514  "Buffer still in use while shutting down BufferProvider? ");
515  }
516  };
517 
518  }//namespace metadata
519 
520 
521 
522 
523 
524  /* ===== Buffer Metadata Frontend ===== */
525 
541  {
542  Literal id_;
543  HashVal family_;
544 
545  metadata::Table table_;
547 
548  public:
549  typedef metadata::Key Key;
550  typedef metadata::Entry Entry;
551 
559  BufferMetadata (Literal implementationID)
560  : id_(implementationID)
561  , family_(hash_value(id_))
562  { }
563 
574  Key
575  key ( size_t storageSize
576  , TypeHandler instanceFunc =RAW_BUFFER
577  , LocalKey specifics =UNSPECIFIC)
578  {
579  REQUIRE (storageSize);
580  Key typeKey = trackKey (family_, storageSize);
581 
582  if (nontrivial(instanceFunc))
583  typeKey = trackKey (typeKey, instanceFunc);
584 
585  if (nontrivial(specifics))
586  typeKey = trackKey (typeKey, specifics);
587 
588  return typeKey;
589  }
590 
592  Key
593  key (Key const& parentKey, TypeHandler const& instanceFunc)
594  {
595  return trackKey (parentKey, instanceFunc);
596  }
597 
600  Key
601  key (Key const& parentKey, LocalKey specifics)
602  {
603  return trackKey (parentKey, specifics);
604  }
605 
610  Key const&
611  key (Key const& parentKey, void* concreteBuffer, LocalKey const& implID =UNSPECIFIC)
612  {
613  Key derivedKey = Key::forEntry (parentKey, concreteBuffer);
614  Entry* existing = table_.fetch (derivedKey);
615 
616  return existing? *existing
617  : markLocked (parentKey,concreteBuffer,implID);
618  }
619 
642  Entry&
643  lock (Key const& parentKey
644  ,void* concreteBuffer
645  ,LocalKey const& implID =UNSPECIFIC
646  ,bool onlyNew =false)
647  {
648  if (!concreteBuffer)
649  throw error::Invalid{"Attempt to lock a slot for a NULL buffer"
650  , LERR_(BOTTOM_VALUE)};
651 
652  Entry newEntry(parentKey, concreteBuffer, implID);
653  Entry* existing = table_.fetch (newEntry);
654 
655  if (existing && onlyNew)
656  throw error::Logic{"Attempt to lock a slot for a new buffer, "
657  "while actually the old buffer is still locked"
658  , LERR_(LIFECYCLE)};
659  if (existing && existing->isLocked())
660  throw error::Logic{"Attempt to re-lock a buffer still in use"
661  , LERR_(LIFECYCLE)};
662 
663  if (!existing)
664  return store_and_lock (newEntry); // actual creation
665  else
666  return existing->lock (concreteBuffer);
667  }
668 
677  Entry&
678  get (HashVal hashID)
679  {
680  Entry* entry = table_.fetch (hashID);
681  if (!entry)
682  throw error::Invalid ("Attempt to access an unknown buffer metadata entry");
683 
684  return *entry;
685  }
686 
687  bool
688  isKnown (HashVal key) const
689  {
690  return bool(table_.fetch (key));
691  }
692 
693  bool
694  isLocked (HashVal key) const
695  {
696  const Entry* entry = table_.fetch (key);
697  return entry
698  && entry->isLocked();
699  }
700 
701 
702 
703  /* == memory management operations == */
704 
715  Entry&
716  markLocked (Key const& parentKey, void* buffer, LocalKey const& implID =UNSPECIFIC)
717  {
718  if (!buffer)
719  throw error::Fatal{"Attempt to lock for a NULL buffer. Allocation floundered?"
720  , LERR_(BOTTOM_VALUE)};
721 
722  return this->lock(parentKey, buffer, implID, true); // force creation of a new entry
723  }
724 
728  void
730  {
731  Entry* entry = table_.fetch (key);
732  if (!entry) return;
733 
734  ASSERT (entry && (key == HashVal(*entry)));
735  release (*entry);
736  }
737 
738  void
739  release (Entry const& entry)
740  {
741  if (FREE != entry.state())
742  throw error::Logic{"Attempt to release a buffer still in use"
743  , LERR_(LIFECYCLE)};
744 
745  table_.remove (HashVal(entry));
746  }
747 
748 
749 
750  private:
751 
752  template<typename PAR, typename DEF>
753  Key
754  trackKey (PAR parent, DEF specialisation)
755  {
756  Key newKey (parent,specialisation);
757  maybeStore (newKey);
758  return newKey;
759  }
760 
761  void
762  maybeStore (Key const& key)
763  {
764  if (isKnown (key)) return;
765  table_.store (Entry (key, NULL));
766  }
767 
768  Entry&
769  store_and_lock (Entry const& metadata)
770  {
771  Entry& newEntry = table_.store (metadata);
772  try
773  {
774  newEntry.invokeEmbeddedCtor();
775  ENSURE (LOCKED == newEntry.state());
776  ENSURE (newEntry.access());
777  }
778  catch(...)
779  {
780  newEntry.mark(FREE);
781  throw;
782  }
783  return newEntry;
784  }
785  };
786 
787 
788 
789 
790 
791 }} // namespace steam::engine
792 #endif
allocated buffer, no longer in use
A marker data type used in metadata / buffer management of the render engine.
Key const & key(Key const &parentKey, void *concreteBuffer, LocalKey const &implID=UNSPECIFIC)
shortcut to access the Key part of a (probably new) Entry describing a concrete buffer at the given a...
Entry & store(Entry const &newEntry)
store a copy of the given new metadata entry.
Entry & markLocked(Key const &parentKey, void *buffer, LocalKey const &implID=UNSPECIFIC)
combine the type (Key) with a concrete buffer, thereby marking this buffer as locked.
(Hash)Table to store and manage buffer metadata.
#define ERROR_LOG_AND_IGNORE(_FLAG_, _OP_DESCR_)
convenience shortcut for a sequence of catch blocks just logging and consuming an error...
Definition: error.hpp:275
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Any copy and copy construction prohibited.
Definition: nocopy.hpp:46
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:85
bool isTypeKey() const
is this Entry just an (abstract) placeholder for a type?
void release(HashVal key)
purge the bare metadata Entry from the metadata tables.
allocated buffer blocked by protocol failure
This header is for including and configuring NoBug.
Key key(Key const &parentKey, TypeHandler const &instanceFunc)
create a sub-type, using a different type/handler functor
allocated buffer, returned from client
allocated buffer actively in use
Entry & mark(BufferState newState)
Buffer state machine.
Steam-Layer implementation namespace root.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Key(Key const &parent, LocalKey anotherTypeSpecificInternalID)
create a derived buffer type description.
Mix-Ins to allow or prohibit various degrees of copying and cloning.
an opaque ID to be used by the BufferProvider implementation.
Description of a Buffer-"type".
Marker types to indicate a literal string and a Symbol.
void for_each(CON const &elements, FUN function, P1 &&bind1, ARGS &&...args)
Accept binding for arbitrary function arguments.
bool isLocked() const
is this Entry currently associated to a concrete buffer? Is this buffer in use?
Helper holding a pair of type-build-up and destruction functors.
Key key(size_t storageSize, TypeHandler instanceFunc=RAW_BUFFER, LocalKey specifics=UNSPECIFIC)
combine the distinguishing properties into a single type key, which will be known/remembered from tha...
Key key(Key const &parentKey, LocalKey specifics)
create a sub-type, using a different private-ID (implementation defined)
Lumiera error handling (C++ interface).
Entry & lock(Key const &parentKey, void *concreteBuffer, LocalKey const &implID=UNSPECIFIC, bool onlyNew=false)
core operation to access or create a concrete buffer metadata entry.
Hash value types and utilities.
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:56
BufferMetadata(Literal implementationID)
establish a metadata registry.
Key(Key const &parent, size_t differingStorageSize)
create a derived buffer type description.
A pair of functors to maintain a datastructure within a buffer.
BufferState
Buffer states usable within BufferProvider and stored within the metadata.
abstract entry, not yet allocated
Registry for managing buffer metadata.
Entry * fetch(HashVal hashID)
fetch metadata record, if any
A complete metadata Entry, based on a Key.
Perform operations "for each element" of a collection.
Key(Key const &parent, TypeHandler const &differingTypeHandlerFunctions)
create a derived buffer type description.
static Key forEntry(Key const &parent, const void *bufferAddr, LocalKey const &implID=UNSPECIFIC)
build derived Key for a concrete buffer Entry
Key(HashVal familyID, size_t storageSize)
build a standard basic key describing a kind of Buffer.