#pragma once
#include "BPlatform.h"
#include "DeferredDecommit.h"
#include "Mutex.h"
#include "PerProcess.h"
#include "Vector.h"
#include <chrono>
#include <condition_variable>
#include <mutex>
#if BOS(DARWIN)
#include <dispatch/dispatch.h>
#endif
namespace bmalloc {
class Scavenger {
public:
BEXPORT Scavenger(std::lock_guard<Mutex>&);
~Scavenger() = delete;
void scavenge();
#if BOS(DARWIN)
void setScavengerThreadQOSClass(qos_class_t overrideClass) { m_requestedScavengerThreadQOSClass = overrideClass; }
qos_class_t requestedScavengerThreadQOSClass() const { return m_requestedScavengerThreadQOSClass; }
#endif
bool willRun() { return m_state == State::Run; }
void run();
bool willRunSoon() { return m_state > State::Sleep; }
void runSoon();
BEXPORT void didStartGrowing();
BEXPORT void scheduleIfUnderMemoryPressure(size_t bytes);
BEXPORT void schedule(size_t bytes);
size_t freeableMemory();
size_t footprint();
void enableMiniMode();
private:
enum class State { Sleep, Run, RunSoon };
void runHoldingLock();
void runSoonHoldingLock();
void scheduleIfUnderMemoryPressureHoldingLock(size_t bytes);
BNO_RETURN static void threadEntryPoint(Scavenger*);
BNO_RETURN void threadRunLoop();
void setSelfQOSClass();
void setThreadName(const char*);
std::chrono::milliseconds timeSinceLastFullScavenge();
std::chrono::milliseconds timeSinceLastPartialScavenge();
void partialScavenge();
std::atomic<State> m_state { State::Sleep };
size_t m_scavengerBytes { 0 };
bool m_isProbablyGrowing { false };
Mutex m_mutex;
Mutex m_scavengingMutex;
std::condition_variable_any m_condition;
std::thread m_thread;
std::chrono::steady_clock::time_point m_lastFullScavengeTime { std::chrono::steady_clock::now() };
std::chrono::steady_clock::time_point m_lastPartialScavengeTime { std::chrono::steady_clock::now() };
#if BOS(DARWIN)
dispatch_source_t m_pressureHandlerDispatchSource;
qos_class_t m_requestedScavengerThreadQOSClass { QOS_CLASS_USER_INITIATED };
#endif
Vector<DeferredDecommit> m_deferredDecommits;
bool m_isInMiniMode { false };
};
}