#pragma once #include #include #include #include #include #include #include #include namespace DB { class RWLockFIFO; using RWLockFIFOPtr = std::shared_ptr; /// Implements shared lock with FIFO service /// You could call it recursively (several calls from the same thread) in Read mode class RWLockFIFO : public std::enable_shared_from_this { public: enum Type { Read, Write }; private: /// Client is that who wants to acquire the lock. struct Client { explicit Client(const std::string & info = {}) : info{info} {} bool isStarted() { return start_time != 0; } /// TODO: delete extra info below if there is no need fot it already. std::string info; int thread_number = 0; std::time_t enqueue_time = 0; std::time_t start_time = 0; Type type = Read; }; public: static RWLockFIFOPtr create() { return RWLockFIFOPtr(new RWLockFIFO); } /// Just use LockHandler::reset() to release the lock class LockHandlerImpl; friend class LockHandlerImpl; using LockHandler = std::shared_ptr; /// Waits in the queue and returns appropriate lock LockHandler getLock(Type type, Client client = Client{}); LockHandler getLock(Type type, const std::string & who) { return getLock(type, Client(who)); } using Clients = std::vector; /// Returns list of executing and waiting clients Clients getClientsInTheQueue() const; private: RWLockFIFO() = default; struct Group; using GroupsContainer = std::list; using ClientsContainer = std::list; using ThreadToHandler = std::map>; /// Group of clients that should be executed concurrently /// i.e. a group could contain several readers, but only one writer struct Group { const Type type; ClientsContainer clients; std::condition_variable cv; /// all clients of the group wait group condvar explicit Group(Type type) : type{type} {} }; mutable std::mutex mutex; GroupsContainer queue; ThreadToHandler thread_to_handler; }; }