#ifndef FTXUI_COMPONENTS_CONSUMER_PRODUCER_H_ #define FTXUI_COMPONENTS_CONSUMER_PRODUCER_H_ #include #include #include #include #include #include namespace ftxui { // Usage: // // Initialization: // --------------- // // auto consumer = MakeConsumer(); // auto producer_1 = consumer.MakeProducer(); // auto producer_2 = consumer.MakeProducer(); // // Then move one producers elsewhere, potentially in a different thread. // ---------------------- // [thread 1] producer_1->Send("hello"); // [thread 2] producer_2->Send("world"); // // On the consumer side: // --------------------- // char c; // while(consumer_->Receive(&c)) // Return true as long as there is a producer. // print(c) // // Consumer::Receive returns true when the last Producer is released. // clang-format off template class ProducerImpl; template class ConsumerImpl; template using Producer = std::unique_ptr>; template using Consumer = std::unique_ptr>; template Consumer MakeConsumer(); // clang-format on // ---- Implementation part ---- template class ProducerImpl { public: void Send(T t) { consumer_->Receive(std::move(t)); } ~ProducerImpl() { consumer_->producers_--; } private: friend class ConsumerImpl; ProducerImpl(ConsumerImpl* consumer) : consumer_(consumer) {} ConsumerImpl* consumer_; }; template class ConsumerImpl { public: Producer MakeProducer() { producers_++; return std::unique_ptr>(new ProducerImpl(this)); } bool Receive(T* t) { while (producers_) { std::unique_lock lock(mutex_); while (queue_.empty()) notifier_.wait(lock); if (queue_.empty()) continue; *t = std::move(queue_.front()); queue_.pop(); return true; } return false; } private: friend class ProducerImpl; void Receive(T t) { std::unique_lock lock(mutex_); queue_.push(std::move(t)); notifier_.notify_one(); } std::mutex mutex_; std::queue queue_; std::condition_variable notifier_; std::atomic producers_ = 0; }; template Consumer MakeConsumer() { return std::make_unique>(); } } // namespace ftxui #endif // FTXUI_COMPONENTS_CONSUMER_PRODUCER_H_