1
0
Fork 0
Univerxel/src/data/safe_priority_queue.hpp

158 lines
4.8 KiB
C++
Raw Normal View History

2020-07-22 20:55:13 +00:00
#pragma once
#include <tuple>
#include <vector>
2020-07-23 19:39:08 +00:00
#include <robin_hood.h>
2020-07-22 20:55:13 +00:00
#include <mutex>
#include <condition_variable>
#include <algorithm>
2020-08-07 17:08:02 +00:00
#include <Tracy.hpp> // NOLINT
2020-07-22 20:55:13 +00:00
2020-07-25 16:45:03 +00:00
namespace data {
/// Thread safe queue with unique keys updating priority and value
2020-08-02 20:15:53 +00:00
template <class K, class V, class W, class hash = robin_hood::hash<K>>
2020-07-25 16:45:03 +00:00
class safe_priority_queue_map {
private:
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
return a.second < b.second;
}
std::vector<std::pair<K, W>> heap;
2020-08-02 20:15:53 +00:00
robin_hood::unordered_map<K, V, hash> map;
2020-08-07 17:08:02 +00:00
TracyLockableN(std::mutex, mutex, "PriorityQueueMap");
std::condition_variable_any cv;
2020-07-25 16:45:03 +00:00
public:
2020-08-08 11:01:07 +00:00
std::pair<std::function<void(const K&, const V&, const W&)>, std::unique_lock<LockableBase(std::mutex)>> inserter() {
return std::make_pair([&](const K& key, const V& val, const W& weight) {
heap.emplace_back(key, weight);
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
map.insert_or_assign(key, val);
}, std::unique_lock<LockableBase(std::mutex)>(mutex));
}
2020-07-25 16:45:03 +00:00
void push(const K& key, const V& val, const W& weight) {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-31 17:09:44 +00:00
heap.emplace_back(key, weight);
2020-07-25 16:45:03 +00:00
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
map.insert_or_assign(key, val);
cv.notify_one();
}
bool pop(std::pair<K, V>& out) {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
if (heap.empty())
return false;
std::pop_heap(heap.begin(), heap.end(), cmpByWeight);
const auto priority = heap.back();
heap.pop_back();
const auto it = map.find(priority.first);
if(it == map.end())
return false;
out = std::make_pair(it->first, it->second);
map.erase(it);
return true;
}
bool empty() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
return heap.empty();
}
size_t size() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
return map.size();
}
2020-08-05 13:14:57 +00:00
void notify_all() {
2020-07-25 16:45:03 +00:00
cv.notify_all();
}
2020-08-05 13:14:57 +00:00
void notify_one() {
cv.notify_one();
}
2020-07-25 16:45:03 +00:00
void wait() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
if (heap.empty())
cv.wait(lock);
}
};
/// Thread safe queue with unique keys updating priority
template <class K, class W>
class safe_priority_queue {
private:
static bool cmpByWeight(const std::pair<K, W> &a, const std::pair<K, W> &b) {
return a.second < b.second;
}
std::vector<std::pair<K, W>> heap;
2020-07-26 20:53:14 +00:00
robin_hood::unordered_flat_set<K> set;
2020-08-07 17:08:02 +00:00
TracyLockableN(std::mutex, mutex, "PriorityQueue");
std::condition_variable_any cv;
2020-07-25 16:45:03 +00:00
public:
2020-08-08 11:01:07 +00:00
std::pair<std::function<void(const K&, const W&)>, std::unique_lock<LockableBase(std::mutex)>> inserter() {
return std::make_pair([&](const K& key, const W& weight) {
heap.emplace_back(key, weight);
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
set.insert(key);
}, std::unique_lock<LockableBase(std::mutex)>(mutex));
}
2020-07-25 16:45:03 +00:00
void push(const K& key, const W& weight) {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-31 17:09:44 +00:00
heap.emplace_back(key, weight);
2020-07-25 16:45:03 +00:00
std::push_heap(heap.begin(), heap.end(), cmpByWeight);
set.insert(key);
cv.notify_one();
}
bool pop(K& out) {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
if (heap.empty())
return false;
std::pop_heap(heap.begin(), heap.end(), cmpByWeight);
const auto priority = heap.back();
heap.pop_back();
const auto it = set.find(priority.first);
if(it == set.end())
return false;
out = *it;
set.erase(it);
return true;
}
bool empty() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
return heap.empty();
}
size_t size() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
return set.size();
}
2020-08-05 13:14:57 +00:00
void notify_all() {
2020-07-25 16:45:03 +00:00
cv.notify_all();
}
2020-08-05 13:14:57 +00:00
void notify_one() {
cv.notify_one();
}
2020-07-25 16:45:03 +00:00
void wait() {
2020-08-07 17:08:02 +00:00
std::unique_lock<LockableBase(std::mutex)> lock(mutex);
2020-07-25 16:45:03 +00:00
if (heap.empty())
cv.wait(lock);
}
};
}