#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADING_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_THREADING_H
#include "support/Context.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/Twine.h"
#include <atomic>
#include <cassert>
#include <condition_variable>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>
namespace clang {
namespace clangd {
class Semaphore {
public:
Semaphore(std::size_t MaxLocks);
bool try_lock();
void lock();
void unlock();
private:
std::mutex Mutex;
std::condition_variable SlotsChanged;
std::size_t FreeSlots;
};
class Deadline {
public:
Deadline(std::chrono::steady_clock::time_point Time)
: Type(Finite), Time(Time) {}
static Deadline zero() { return Deadline(Zero); }
static Deadline infinity() { return Deadline(Infinite); }
std::chrono::steady_clock::time_point time() const {
assert(Type == Finite);
return Time;
}
bool expired() const {
return (Type == Zero) ||
(Type == Finite && Time < std::chrono::steady_clock::now());
}
bool operator==(const Deadline &Other) const {
return (Type == Other.Type) && (Type != Finite || Time == Other.Time);
}
private:
enum Type { Zero, Infinite, Finite };
Deadline(enum Type Type) : Type(Type) {}
enum Type Type;
std::chrono::steady_clock::time_point Time;
};
Deadline timeoutSeconds(std::optional<double> Seconds);
void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,
Deadline D);
template <typename Func>
[[nodiscard]] bool wait(std::unique_lock<std::mutex> &Lock,
std::condition_variable &CV, Deadline D, Func F) {
while (!F()) {
if (D.expired())
return false;
wait(Lock, CV, D);
}
return true;
}
class Notification {
public:
void notify();
void wait() const { (void)wait(Deadline::infinity()); }
[[nodiscard]] bool wait(Deadline D) const;
private:
bool Notified = false;
mutable std::condition_variable CV;
mutable std::mutex Mu;
};
class AsyncTaskRunner {
public:
~AsyncTaskRunner();
void wait() const { (void)wait(Deadline::infinity()); }
[[nodiscard]] bool wait(Deadline D) const;
void runAsync(const llvm::Twine &Name, llvm::unique_function<void()> Action);
private:
mutable std::mutex Mutex;
mutable std::condition_variable TasksReachedZero;
std::size_t InFlightTasks = 0;
};
template <typename T>
std::future<T> runAsync(llvm::unique_function<T()> Action) {
return std::async(
std::launch::async,
[](llvm::unique_function<T()> &&Action, Context Ctx) {
WithContext WithCtx(std::move(Ctx));
return Action();
},
std::move(Action), Context::current().clone());
}
template <typename Container> class Memoize {
mutable Container Cache;
std::unique_ptr<std::mutex> Mu;
public:
Memoize() : Mu(std::make_unique<std::mutex>()) {}
template <typename T, typename Func>
typename Container::mapped_type get(T &&Key, Func Compute) const {
{
std::lock_guard<std::mutex> Lock(*Mu);
auto It = Cache.find(Key);
if (It != Cache.end())
return It->second;
}
auto V = Compute();
{
std::lock_guard<std::mutex> Lock(*Mu);
auto R = Cache.try_emplace(std::forward<T>(Key), V);
if (!R.second)
return R.first->second;
}
return V;
}
};
class PeriodicThrottler {
using Stopwatch = std::chrono::steady_clock;
using Rep = Stopwatch::duration::rep;
Rep Period;
std::atomic<Rep> Next;
public:
PeriodicThrottler(Stopwatch::duration Period, Stopwatch::duration Delay = {})
: Period(Period.count()),
Next((Stopwatch::now() + Delay).time_since_epoch().count()) {}
bool operator()();
};
}
}
#endif