From 10860cc0ea69dcba1a06dc0a7370f78984eaf525 Mon Sep 17 00:00:00 2001 From: Roker Date: Fri, 19 Oct 2018 12:04:36 +0200 Subject: [PATCH] add Producer/Consumer container. 1st try. Untested! Needs discussion! --- pc_container.hh | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 pc_container.hh diff --git a/pc_container.hh b/pc_container.hh new file mode 100644 index 0000000..a766b4a --- /dev/null +++ b/pc_container.hh @@ -0,0 +1,112 @@ +#ifndef PEP_LIB_PC_CONTAINER +#define PEP_LIB_PC_CONTAINER + +// Conainer adapter that contains a container and a producer/consume queue +// that holds references to all changed elements + + +#include "locked_queue.hh" + +namespace pEp +{ + +// Producer/Consumer container. +// +// The "Producer" works on a std::list: inserts, changes, erases elements and +// informs a "Consumer" via a queue about the changes. +// +// The "Consumer" can poll for changes and process them asynchronously. +// +// Pdata = data of the producer. May be read by the consumer. +// Cdata = data of the consumer. +template +class pc_container +{ +public: + struct PC + { + Pdata* pdata; // data of the producer. Will be nullptr for erased elements + Cdata* cdata; // data of the consumer. + }; + + typedef std::list Container; + + Container::const_iterator begin() const noexcept { return c.cbegin(); } + Container::const_iterator end() const noexcept { return c.cend(); } + std::size_t size() const noexcept { return c.size(); } + bool empty() const noexcept { return c.empty(); } + + + ////////////////////////////////// + // Producer's API: + ////////////////////////////////// + + void insert(Pdata* pd) + { + c.emplace_back(pd, nullptr); + changed.push_back( c.back() ); + } + + // Beware: does not delete *pdata nor *cdata! That's producer's and consumer's task! + // pos->pdata shall already be nullptr, so consumer can detect erasure. + void erase(Container::const_iterator pos) + { + changed.push_back( *pos ); + c.erase(pos); + } + + // notify Consumer about the changed element + void change(Container::const_iterator pos) + { + changed.push_back( *pos ); + } + + // clear the container. Delete all *pdata via custom deleter functor. + void clear(std::function deleter) + { + for(auto q=begin(); q!=end(); ++q) + { + deleter(q->pdata); + q->pdata = nullptr; + erase(q); + } + } + + ////////////////////////////////// + // Consumer's API: + ////////////////////////////////// + + std::size_t changed_elements() const { return changed.size(); } + bool has_changed() const { return !changed.empty(); } + + // got oldest element in change queue. + // + // pc.pdata != nullptr && pc.cdata == nullptr : freshly inserted element + // pc.pdata != nullptr && pc.cdata != nullptr : changed element + // pc.pdata == nullptr && pc.cdata != nullptr : erased element + // + // Beware: this blocks if changed queue is empty! + PC get_changed() + { + PC pc = changed.pop_front(); + + // does the element still exists? + if( pc.pdata && + std::find_if( c.cbegin(), c.cend(), [&pc](PC& element) { return pc.pdata == element.pdata; } ) == c.cend() + ) + { + // No, not anymore. So mark it as erased element for the consumer: + pc.pdata = nullptr; + } + + return pc; + } + +private: + Container c; + locked_queue changed; +}; + +} // end of namespace pEp + +#endif // PEP_LIB_PC_CONTAINER