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)
5  2011, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
44 #ifndef STEAM_ENGINE_BUFFR_METADATA_H
45 #define STEAM_ENGINE_BUFFR_METADATA_H
46 
47 
48 #include "lib/error.hpp"
49 #include "lib/symbol.hpp"
50 #include "lib/hash-value.h"
51 #include "lib/util-foreach.hpp"
52 #include "include/logging.h"
53 #include "steam/streamtype.hpp"
56 #include "lib/nocopy.hpp"
57 
58 #include <unordered_map>
59 
60 
61 namespace steam {
62 namespace engine {
63 
64  using lib::HashVal;
65  using lib::Literal;
66  using util::for_each;
67 
68  namespace error = lumiera::error;
69 
70  namespace metadata {
71  using Buff = StreamType::ImplFacade::DataBuffer;
72 
73  class Key;
74  class Entry;
75  }
76  class BufferMetadata;
77 
78 
79 
80 
87  { NIL,
88  FREE,
92  };
93 
94 
95 
96 
97 
98 
99  namespace { // internal constants to mark the default case
100 
101  inline bool
102  nontrivial (TypeHandler const& toVerify)
103  {
104  return TypeHandler::RAW != toVerify;
105  }
106 
107  inline bool
108  nontrivial (LocalTag const& toVerify)
109  {
110  return LocalTag::UNKNOWN != toVerify;
111  }
112  }
113 
114 
115 
116  /* === Metadata Implementation === */
117 
118  namespace metadata {
119 
120  namespace { // details of hash calculation
121  template<typename VAL>
122  HashVal
123  chainedHash(HashVal accumulatedHash, VAL changedValue)
124  {
125  boost::hash_combine (accumulatedHash, changedValue);
126  return accumulatedHash;
127  }
128  }
129 
130 
137  class Key
138  {
139  HashVal parent_;
140  HashVal hashID_;
141 
142  protected:
143  size_t storageSize_;
144  TypeHandler instanceFunc_;
145  LocalTag specifics_;
146 
147 
148  public:
155  Key (HashVal familyID, size_t storageSize)
156  : parent_(familyID)
157  , hashID_(chainedHash (familyID, storageSize))
158  , storageSize_(storageSize)
159  , instanceFunc_(TypeHandler::RAW)
160  , specifics_(LocalTag::UNKNOWN)
161  { }
162 
163  // standard copy operations permitted
164 
169  Key (Key const& parent, size_t differingStorageSize)
170  : parent_(parent.hashID_)
171  , hashID_(chainedHash (parent_, differingStorageSize))
172  , storageSize_(differingStorageSize) // differing from parent
173  , instanceFunc_(parent.instanceFunc_)
174  , specifics_(parent.specifics_)
175  { }
176 
177 
182  Key (Key const& parent, TypeHandler const& differingTypeHandlerFunctions)
183  : parent_(parent.hashID_)
184  , hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
185  , storageSize_(parent.storageSize_)
186  , instanceFunc_(differingTypeHandlerFunctions) // differing from parent
187  , specifics_(parent.specifics_)
188  { }
189 
190 
195  Key (Key const& parent, LocalTag anotherTypeSpecificInternalTag)
196  : parent_(parent.hashID_)
197  , hashID_(chainedHash (parent_, anotherTypeSpecificInternalTag))
198  , storageSize_(parent.storageSize_)
199  , instanceFunc_(parent.instanceFunc_)
200  , specifics_(anotherTypeSpecificInternalTag) // differing from parent
201  { }
202 
203 
210  static Key
211  forEntry (Key const& parent, const Buff* bufferAddr, LocalTag const& localTag =LocalTag::UNKNOWN)
212  {
213  Key newKey{parent}; // copy of parent as baseline
214  if (nontrivial(localTag))
215  {
216  if (nontrivial(parent.specifics_))
217  throw error::Logic{"Implementation defined local key should not be overridden. "
218  "Underlying buffer type already defines a nontrivial LocalTag"};
219  newKey.parent_ = HashVal(parent);
220  newKey.hashID_ = chainedHash(newKey.hashID_, localTag);
221  newKey.specifics_ = localTag;
222  }
223  if (bufferAddr)
224  {
225  newKey.parent_ = HashVal(parent);
226  newKey.hashID_ = chainedHash(newKey.hashID_, bufferAddr);
227  }
228  return newKey;
229  }
230 
231  void
232  useTypeHandlerFrom (Key const& ref)
233  {
234  if (nontrivial(this->instanceFunc_))
235  throw error::Logic ("unable to supersede an already attached TypeHandler"
236  , LERR_(LIFECYCLE));
237  instanceFunc_ = ref.instanceFunc_;
238  }
239 
240 
241  LocalTag const& localTag() const { return specifics_;}
242  size_t storageSize() const { return storageSize_; }
243 
244  HashVal parentKey() const { return parent_;}
245  operator HashVal() const { return hashID_;}
246  };
247 
248 
263  class Entry
264  : public Key
265  {
266  BufferState state_;
267  Buff* buffer_;
268 
269  protected:
270  Entry (Key const& parent
271  ,Buff* bufferPtr =nullptr
272  ,LocalTag const& specialTag =LocalTag::UNKNOWN
273  )
274  : Key{Key::forEntry (parent, bufferPtr, specialTag)}
275  , state_{bufferPtr? LOCKED:NIL}
276  , buffer_{bufferPtr}
277  { }
278 
281 
282  // standard copy operations permitted
283 
284  public:
287  bool
288  isLocked() const
289  {
290  ASSERT (!buffer_ or (NIL != state_ and FREE != state_));
291  return bool(buffer_);
292  }
293 
297  bool
298  isTypeKey() const
299  {
300  return NIL == state_ and not buffer_;
301  }
302 
303 
305  state() const
306  {
307  return state_;
308  }
309 
310  Buff*
311  access()
312  {
313  __must_not_be_NIL();
314  __must_not_be_FREE();
315 
316  ENSURE (buffer_);
317  return buffer_;
318  }
319 
321  Entry&
322  mark (BufferState newState)
323  {
324  __must_not_be_NIL();
325 
326  if ( (state_ == FREE and newState == LOCKED)
327  or (state_ == LOCKED and newState == EMITTED)
328  or (state_ == LOCKED and newState == BLOCKED)
329  or (state_ == LOCKED and newState == FREE)
330  or (state_ == EMITTED and newState == BLOCKED)
331  or (state_ == EMITTED and newState == FREE)
332  or (state_ == BLOCKED and newState == FREE))
333  {
334  // allowed transition
335  if (newState == FREE)
336  invokeEmbeddedDtor_and_clear();
337  if (newState == LOCKED)
338  invokeEmbeddedCtor();
339  state_ = newState;
340  return *this;
341  }
342 
343  throw error::Fatal ("Invalid buffer state transition.");
344  }
345 
346  Entry&
347  lock (Buff* newBuffer)
348  {
349  __must_be_FREE();
350  buffer_ = newBuffer;
351  return mark (LOCKED);
352  }
353 
354  Entry&
355  invalidate (bool invokeDtor =true)
356  {
357  if (buffer_ and invokeDtor)
358  invokeEmbeddedDtor_and_clear();
359  buffer_ = nullptr;
360  state_ = FREE;
361  return *this;
362  }
363 
364 
365  protected:
369  void
371  {
372  __buffer_required();
373  if (nontrivial (instanceFunc_))
374  instanceFunc_.createAttached (buffer_);
375  }
376 
380  void
382  {
383  __buffer_required();
384  if (nontrivial (instanceFunc_))
385  instanceFunc_.destroyAttached (buffer_);
386  buffer_ = 0;
387  }
388 
389  private:
390  void
391  __must_not_be_NIL() const
392  {
393  if (NIL == state_)
394  throw error::Fatal ("Buffer metadata entry with state==NIL encountered."
395  "State transition logic broken (programming error)"
396  , LERR_(LIFECYCLE));
397  }
398 
399  void
400  __must_not_be_FREE() const
401  {
402  if (FREE == state_)
403  throw error::Logic ("Buffer is inaccessible (marked as free). "
404  "Need a new buffer pointer in order to lock an entry. "
405  "You should invoke markLocked(buffer) prior to access."
406  , LERR_(LIFECYCLE));
407  }
408 
409  void
410  __must_be_FREE() const
411  {
412  if (FREE != state_)
413  throw error::Logic ("Buffer already in use"
414  , LERR_(LIFECYCLE));
415  REQUIRE (!buffer_, "Buffer marked as free, "
416  "but buffer pointer is set.");
417  }
418 
419  void
420  __buffer_required() const
421  {
422  if (!buffer_)
423  throw error::Fatal ("Need concrete buffer for any further operations");
424  }
425  };
426 
427 
428 
438  class Table
439  {
440  typedef std::unordered_map<HashVal,Entry> MetadataStore;
441 
442  MetadataStore entries_;
443 
444  public:
445  ~Table() { verify_all_buffers_freed(); }
446 
451  Entry*
452  fetch (HashVal hashID)
453  {
454  MetadataStore::iterator pos = entries_.find (hashID);
455  if (pos != entries_.end())
456  return &(pos->second);
457  else
458  return NULL;
459  }
460 
461  const Entry*
462  fetch (HashVal hashID) const
463  {
464  MetadataStore::const_iterator pos = entries_.find (hashID);
465  if (pos != entries_.end())
466  return &(pos->second);
467  else
468  return NULL;
469  }
470 
479  Entry&
480  store (Entry const& newEntry)
481  {
482  REQUIRE (!fetch (newEntry), "duplicate buffer metadata entry");
483  MetadataStore::iterator pos = entries_.emplace (HashVal(newEntry), newEntry)
484  .first;
485 
486  ENSURE (pos != entries_.end());
487  return pos->second;
488  }
489 
490  void
491  remove (HashVal hashID)
492  {
493  uint cnt = entries_.erase (hashID);
494  ENSURE (cnt, "entry to remove didn't exist");
495  }
496 
497  private:
498  void
499  verify_all_buffers_freed()
500  try
501  {
502  for_each (entries_, verify_is_free);
503  }
504  ERROR_LOG_AND_IGNORE (engine,"Shutdown of BufferProvider metadata store")
505 
506  static void
507  verify_is_free (std::pair<HashVal, Entry> const& e)
508  {
509  WARN_IF (e.second.isLocked(), engine,
510  "Buffer still in use while shutting down BufferProvider? ");
511  }
512  };
513 
514  }//namespace metadata
515 
516 
517 
518 
519 
520  /* ===== Buffer Metadata Frontend ===== */
521 
537  {
538  Literal id_;
539  HashVal family_;
540 
541  metadata::Table table_;
543 
544  public:
545  using Key = metadata::Key;
546  using Entry = metadata::Entry;
547 
555  BufferMetadata (Literal implementationID)
556  : id_(implementationID)
557  , family_(hash_value(id_))
558  { }
559 
570  Key
571  key ( size_t storageSize
572  , TypeHandler instanceFunc =TypeHandler::RAW
573  , LocalTag specifics =LocalTag::UNKNOWN)
574  {
575  REQUIRE (storageSize);
576  Key typeKey = trackKey (family_, storageSize);
577 
578  if (nontrivial(instanceFunc))
579  typeKey = trackKey (typeKey, instanceFunc);
580 
581  if (nontrivial(specifics))
582  typeKey = trackKey (typeKey, specifics);
583 
584  return typeKey;
585  }
586 
588  Key
589  key (Key const& parentKey, TypeHandler const& instanceFunc)
590  {
591  return trackKey (parentKey, instanceFunc);
592  }
593 
596  Key
597  key (Key const& parentKey, LocalTag specifics)
598  {
599  return trackKey (parentKey, specifics);
600  }
601 
606  Key const&
607  key (Key const& parentKey, metadata::Buff* concreteBuffer, LocalTag const& specifics =LocalTag::UNKNOWN)
608  {
609  Key derivedKey = Key::forEntry (parentKey, concreteBuffer, specifics);
610  Entry* existing = table_.fetch (derivedKey);
611 
612  return existing? *existing
613  : markLocked (parentKey,concreteBuffer,specifics);
614  }
615 
640  Entry&
641  lock (Key const& parentKey
642  ,metadata::Buff* concreteBuffer
643  ,LocalTag const& specifics =LocalTag::UNKNOWN
644  ,bool onlyNew =false)
645  {
646  if (!concreteBuffer)
647  throw error::Invalid{"Attempt to lock a slot for a NULL buffer"
648  , LERR_(BOTTOM_VALUE)};
649 
650  Entry newEntry{parentKey, concreteBuffer, specifics};
651  Entry* existing = table_.fetch (newEntry);
652 
653  if (existing and onlyNew)
654  throw error::Logic{"Attempt to lock a slot for a new buffer, "
655  "while actually the old buffer is still locked"
656  , LERR_(LIFECYCLE)};
657  if (existing and existing->isLocked())
658  throw error::Logic{"Attempt to re-lock a buffer still in use"
659  , LERR_(LIFECYCLE)};
660 
661  if (not existing)
662  return store_as_locked (newEntry); // actual creation
663  else
664  return existing->lock (concreteBuffer);
665  }
666 
675  Entry&
676  get (HashVal hashID)
677  {
678  Entry* entry = table_.fetch (hashID);
679  if (!entry)
680  throw error::Invalid ("Attempt to access an unknown buffer metadata entry");
681 
682  return *entry;
683  }
684 
685  bool
686  isKnown (HashVal key) const
687  {
688  return bool(table_.fetch (key));
689  }
690 
691  bool
692  isLocked (HashVal key) const
693  {
694  const Entry* entry = table_.fetch (key);
695  return entry
696  and entry->isLocked();
697  }
698 
699 
700 
701  /* == memory management operations == */
702 
713  Entry&
714  markLocked (Key const& parentKey, metadata::Buff* buffer, LocalTag const& specifics =LocalTag::UNKNOWN)
715  {
716  if (!buffer)
717  throw error::Fatal{"Attempt to lock for a NULL buffer. Allocation floundered?"
718  , LERR_(BOTTOM_VALUE)};
719 
720  return this->lock (parentKey, buffer, specifics, true); // force creation of a new entry
721  }
722 
726  void
728  {
729  Entry* entry = table_.fetch (key);
730  if (!entry) return;
731 
732  ASSERT (entry and (key == HashVal(*entry)));
733  release (*entry);
734  }
735 
736  void
737  release (Entry const& entry)
738  {
739  if (FREE != entry.state())
740  throw error::Logic{"Attempt to release a buffer still in use"
741  , LERR_(LIFECYCLE)};
742 
743  table_.remove (HashVal(entry));
744  }
745 
746 
747 
748  private:
749 
750  template<typename PAR, typename DEF>
751  Key
752  trackKey (PAR parent, DEF specialisation)
753  {
754  Key newKey{parent, specialisation};
755  maybeStore (newKey);
756  return newKey;
757  }
758 
759  void
760  maybeStore (Key const& key)
761  {
762  if (isKnown (key)) return;
763  table_.store (Entry{key, nullptr});
764  }
765 
773  Entry&
774  store_as_locked (Entry const& metadata)
775  {
776  REQUIRE (metadata.isLocked());
777  Entry& newEntry = table_.store (metadata);
778  try
779  {
780  newEntry.invokeEmbeddedCtor();
781  ENSURE (LOCKED == newEntry.state());
782  ENSURE (newEntry.access());
783  }
784  catch(...)
785  {
786  newEntry.mark(FREE);
787  throw;
788  }
789  return newEntry;
790  }
791  };
792 
793 
794 
795 
796 }} // namespace steam::engine
797 #endif /*STEAM_ENGINE_BUFFR_METADATA_H*/
allocated buffer, no longer in use
Entry & store(Entry const &newEntry)
store a copy of the given new metadata entry.
Framework for classification of media streams.
(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:266
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Key key(Key const &parentKey, LocalTag specifics)
create a sub-type, using a different private-ID (implementation defined)
Any copy and copy construction prohibited.
Definition: nocopy.hpp:37
A marker data type used in metadata / buffer management of the render engine.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
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
an opaque mark to be used by the BufferProvider implementation.
allocated buffer, returned from client
allocated buffer actively in use
Entry & mark(BufferState newState)
Buffer state machine.
Steam-Layer implementation namespace root.
Entry & lock(Key const &parentKey, metadata::Buff *concreteBuffer, LocalTag const &specifics=LocalTag::UNKNOWN, bool onlyNew=false)
core operation to access or create a concrete buffer metadata entry.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Mix-Ins to allow or prohibit various degrees of copying and cloning.
Description of a Buffer-"type".
Marker types to indicate a literal string and a Symbol.
static const TypeHandler RAW
Marker for the default case: raw buffer without type handling.
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?
Key const & key(Key const &parentKey, metadata::Buff *concreteBuffer, LocalTag const &specifics=LocalTag::UNKNOWN)
shortcut to access the Key part of a (probably new) Entry describing a concrete buffer at the given a...
Helper holding a pair of type-build-up and destruction functors.
Lumiera error handling (C++ interface).
Key(Key const &parent, LocalTag anotherTypeSpecificInternalTag)
create a derived buffer type description.
static const LocalTag UNKNOWN
Marker when no distinct local key is given.
Hash value types and utilities.
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:52
BufferMetadata(Literal implementationID)
establish a metadata registry.
Key(Key const &parent, size_t differingStorageSize)
create a derived buffer type description.
static Key forEntry(Key const &parent, const Buff *bufferAddr, LocalTag const &localTag=LocalTag::UNKNOWN)
build derived Key for a concrete buffer Entry
Key key(size_t storageSize, TypeHandler instanceFunc=TypeHandler::RAW, LocalTag specifics=LocalTag::UNKNOWN)
combine the distinguishing properties into a single type key, which will be known/remembered from tha...
A pair of functors to maintain a datastructure within a buffer.
Entry & markLocked(Key const &parentKey, metadata::Buff *buffer, LocalTag const &specifics=LocalTag::UNKNOWN)
combine the type (Key) with a concrete buffer, thereby marking this buffer as locked.
BufferState
Buffer states usable within BufferProvider and stored within the metadata.
abstract entry, not yet allocated
Entry & store_as_locked(Entry const &metadata)
store a fully populated entry immediately starting with locked state
Registry for managing buffer metadata.
Entry * fetch(HashVal hashID)
fetch metadata record, if any
HashVal hash_value(ProcID const &procID)
generate registry hash value based on the distinct data in ProcID.
Definition: proc-node.cpp:105
placeholder type for the contents of a data buffer.
Definition: streamtype.hpp:112
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.
Key(HashVal familyID, size_t storageSize)
build a standard basic key describing a kind of Buffer.