sm6375-common: power-libperfmgr: ADPF: add Early Hint feature
Add Early Hint feature and integrate with Stale Timer Bug: 198379880 Test: build and manual test Change-Id: I17009ee5b9ff922a79ccf5cb68be5b959038267d
This commit is contained in:
parent
362ddc9bd3
commit
938e2b96f7
2 changed files with 148 additions and 54 deletions
|
@ -145,7 +145,7 @@ PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<
|
||||||
int64_t durationNanos) {
|
int64_t durationNanos) {
|
||||||
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
|
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
|
||||||
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
|
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
|
||||||
mStaleHandler = sp<StaleHandler>(new StaleHandler(this));
|
mHintTimerHandler = sp<HintTimerHandler>(new HintTimerHandler(this));
|
||||||
mPowerManagerHandler = PowerSessionManager::getInstance();
|
mPowerManagerHandler = PowerSessionManager::getInstance();
|
||||||
|
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
|
@ -154,8 +154,6 @@ PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<
|
||||||
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
|
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
|
||||||
sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
||||||
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
|
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
|
||||||
sz = StringPrintf("adpf.%s-stale", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), isStale());
|
|
||||||
}
|
}
|
||||||
PowerSessionManager::getInstance()->addPowerSession(this);
|
PowerSessionManager::getInstance()->addPowerSession(this);
|
||||||
// init boost
|
// init boost
|
||||||
|
@ -175,7 +173,7 @@ PowerHintSession::~PowerHintSession() {
|
||||||
sz = sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
sz = sz = StringPrintf("adpf.%s-active", idstr.c_str());
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
ATRACE_INT(sz.c_str(), 0);
|
||||||
}
|
}
|
||||||
mStaleHandler->setSessionDead();
|
mHintTimerHandler->setSessionDead();
|
||||||
delete mDescriptor;
|
delete mDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +184,14 @@ std::string PowerHintSession::getIdString() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::updateUniveralBoostMode() {
|
void PowerHintSession::updateUniveralBoostMode() {
|
||||||
|
if (ATRACE_ENABLED()) {
|
||||||
|
const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", getIdString().c_str());
|
||||||
|
ATRACE_BEGIN(tag.c_str());
|
||||||
|
}
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
|
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
|
||||||
|
if (ATRACE_ENABLED()) {
|
||||||
|
ATRACE_END();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int PowerHintSession::setUclamp(int32_t min, bool update) {
|
int PowerHintSession::setUclamp(int32_t min, bool update) {
|
||||||
|
@ -253,7 +258,7 @@ ndk::ScopedAStatus PowerHintSession::resume() {
|
||||||
if (mDescriptor->is_active.load())
|
if (mDescriptor->is_active.load())
|
||||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||||
mDescriptor->is_active.store(true);
|
mDescriptor->is_active.store(true);
|
||||||
mStaleHandler->updateStaleTimer();
|
mHintTimerHandler->updateHintTimer(0);
|
||||||
// resume boost
|
// resume boost
|
||||||
setUclamp(mDescriptor->current_min, false);
|
setUclamp(mDescriptor->current_min, false);
|
||||||
if (ATRACE_ENABLED()) {
|
if (ATRACE_ENABLED()) {
|
||||||
|
@ -332,8 +337,6 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
|
||||||
sz = StringPrintf("adpf.%s-hint.overtime", idstr.c_str());
|
sz = StringPrintf("adpf.%s-hint.overtime", idstr.c_str());
|
||||||
ATRACE_INT(sz.c_str(),
|
ATRACE_INT(sz.c_str(),
|
||||||
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
|
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
|
||||||
sz = StringPrintf("adpf.%s-stale", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), isStale());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PowerHintMonitor::getInstance()->isRunning() && isStale()) {
|
if (PowerHintMonitor::getInstance()->isRunning() && isStale()) {
|
||||||
|
@ -347,7 +350,7 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mStaleHandler->updateStaleTimer();
|
mHintTimerHandler->updateHintTimer(actualDurations);
|
||||||
|
|
||||||
if (!adpfConfig->mPidOn) {
|
if (!adpfConfig->mPidOn) {
|
||||||
setUclamp(adpfConfig->mUclampMinHigh);
|
setUclamp(adpfConfig->mUclampMinHigh);
|
||||||
|
@ -357,8 +360,6 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
|
||||||
adpfConfig, mDescriptor->duration, actualDurations, &(mDescriptor->integral_error),
|
adpfConfig, mDescriptor->duration, actualDurations, &(mDescriptor->integral_error),
|
||||||
&(mDescriptor->previous_error), getIdString());
|
&(mDescriptor->previous_error), getIdString());
|
||||||
|
|
||||||
mStaleHandler->updateStaleTimer();
|
|
||||||
|
|
||||||
/* apply to all the threads in the group */
|
/* apply to all the threads in the group */
|
||||||
if (output != 0) {
|
if (output != 0) {
|
||||||
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
|
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
|
||||||
|
@ -401,7 +402,7 @@ bool PowerHintSession::isActive() {
|
||||||
|
|
||||||
bool PowerHintSession::isStale() {
|
bool PowerHintSession::isStale() {
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
return now >= mStaleHandler->getStaleTime();
|
return now >= mHintTimerHandler->getStaleTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<int> &PowerHintSession::getTidList() const {
|
const std::vector<int> &PowerHintSession::getTidList() const {
|
||||||
|
@ -409,70 +410,146 @@ const std::vector<int> &PowerHintSession::getTidList() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::setStale() {
|
void PowerHintSession::setStale() {
|
||||||
if (ATRACE_ENABLED()) {
|
|
||||||
const std::string idstr = getIdString();
|
|
||||||
std::string sz = StringPrintf("adpf.%s-stale", idstr.c_str());
|
|
||||||
ATRACE_INT(sz.c_str(), 1);
|
|
||||||
}
|
|
||||||
// Reset to default uclamp value.
|
// Reset to default uclamp value.
|
||||||
setUclamp(0, false);
|
setUclamp(0, false);
|
||||||
// Deliver a task to check if all sessions are inactive.
|
// Deliver a task to check if all sessions are inactive.
|
||||||
updateUniveralBoostMode();
|
updateUniveralBoostMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::StaleHandler::updateStaleTimer() {
|
void PowerHintSession::HintTimerHandler::updateHintTimer(int64_t actualDurationNs) {
|
||||||
std::lock_guard<std::mutex> guard(mStaleLock);
|
std::lock_guard<std::mutex> guard(mStaleLock);
|
||||||
if (PowerHintMonitor::getInstance()->isRunning()) {
|
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
||||||
auto when = getStaleTime();
|
HintTimerState prevState = mState;
|
||||||
auto now = std::chrono::steady_clock::now();
|
mState = MONITORING;
|
||||||
mLastUpdatedTime.store(now);
|
auto now = std::chrono::steady_clock::now();
|
||||||
if (now > when) {
|
mLastUpdatedTime.store(now);
|
||||||
|
nanoseconds nextStartDur = nanoseconds((mSession->mDescriptor->work_period
|
||||||
|
? mSession->mDescriptor->work_period
|
||||||
|
: mSession->mDescriptor->duration.count()) -
|
||||||
|
actualDurationNs);
|
||||||
|
mNextStartTime.store(now + nextStartDur);
|
||||||
|
if (prevState != MONITORING) {
|
||||||
|
int64_t next =
|
||||||
|
static_cast<int64_t>(duration_cast<nanoseconds>(getEarlyBoostTime() - now).count());
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
||||||
|
next, mSession->mHintTimerHandler, NULL);
|
||||||
|
if (prevState == STALE) {
|
||||||
mSession->updateUniveralBoostMode();
|
mSession->updateUniveralBoostMode();
|
||||||
}
|
}
|
||||||
if (!mIsMonitoringStale.load()) {
|
}
|
||||||
auto next = getStaleTime();
|
if (ATRACE_ENABLED()) {
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleHandler);
|
const std::string idstr = mSession->getIdString();
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
std::string sz = StringPrintf("adpf.%s-timer.state", idstr.c_str());
|
||||||
duration_cast<nanoseconds>(next - now).count(), mSession->mStaleHandler, NULL);
|
ATRACE_INT(sz.c_str(), mState);
|
||||||
mIsMonitoringStale.store(true);
|
sz = StringPrintf("adpf.%s-timer.nextvsync", idstr.c_str());
|
||||||
}
|
ATRACE_INT(sz.c_str(), nextStartDur.count());
|
||||||
if (ATRACE_ENABLED()) {
|
sz = StringPrintf("adpf.%s-timer.nexthint", idstr.c_str());
|
||||||
const std::string idstr = mSession->getIdString();
|
ATRACE_INT(sz.c_str(),
|
||||||
std::string sz = StringPrintf("adpf.%s-stale", idstr.c_str());
|
(int64_t)(nextStartDur.count() + mSession->mDescriptor->duration.count() *
|
||||||
ATRACE_INT(sz.c_str(), 0);
|
adpfConfig->mEarlyBoostTimeFactor));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time_point<steady_clock> PowerHintSession::StaleHandler::getStaleTime() {
|
void PowerHintSession::HintTimerHandler::updateHintTimer(
|
||||||
|
const std::vector<WorkDuration> &actualDurations) {
|
||||||
|
if (actualDurations.size() == 0)
|
||||||
|
return;
|
||||||
|
if (actualDurations.size() >= 2) {
|
||||||
|
const WorkDuration &last = actualDurations[actualDurations.size() - 2];
|
||||||
|
mSession->mDescriptor->last_start = last.timeStampNanos - last.durationNanos;
|
||||||
|
ALOGD("last_start initialized by previous.");
|
||||||
|
}
|
||||||
|
const WorkDuration ¤t = actualDurations.back();
|
||||||
|
int64_t curr_start = current.timeStampNanos - current.durationNanos;
|
||||||
|
if (!mSession->mDescriptor->last_start) {
|
||||||
|
mSession->mDescriptor->last_start = curr_start;
|
||||||
|
ALOGD("last_start initialized by current.");
|
||||||
|
updateHintTimer(current.durationNanos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int64_t period = curr_start - mSession->mDescriptor->last_start;
|
||||||
|
mSession->mDescriptor->last_start = curr_start;
|
||||||
|
if (period > 0 && period < mSession->mDescriptor->duration.count() * 2) {
|
||||||
|
// Accounting workload period with moving average for the last 10 workload.
|
||||||
|
mSession->mDescriptor->work_period =
|
||||||
|
0.9 * mSession->mDescriptor->work_period + 0.1 * period;
|
||||||
|
if (ATRACE_ENABLED()) {
|
||||||
|
const std::string idstr = mSession->getIdString();
|
||||||
|
std::string sz = StringPrintf("adpf.%s-timer.period", idstr.c_str());
|
||||||
|
ATRACE_INT(sz.c_str(), period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateHintTimer(current.durationNanos);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_point<steady_clock> PowerHintSession::HintTimerHandler::getEarlyBoostTime() {
|
||||||
|
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
||||||
|
if (!adpfConfig->mEarlyBoostOn) {
|
||||||
|
return getStaleTime();
|
||||||
|
}
|
||||||
|
int64_t earlyBoostTimeoutNs =
|
||||||
|
(int64_t)mSession->mDescriptor->duration.count() * adpfConfig->mEarlyBoostTimeFactor;
|
||||||
|
return mNextStartTime.load() + nanoseconds(earlyBoostTimeoutNs);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_point<steady_clock> PowerHintSession::HintTimerHandler::getStaleTime() {
|
||||||
return mLastUpdatedTime.load() +
|
return mLastUpdatedTime.load() +
|
||||||
nanoseconds(static_cast<int64_t>(
|
nanoseconds(static_cast<int64_t>(
|
||||||
mSession->mDescriptor->duration.count() *
|
mSession->mDescriptor->duration.count() *
|
||||||
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::StaleHandler::handleMessage(const Message &) {
|
PowerHintSession::HintTimerHandler::~HintTimerHandler() {
|
||||||
|
ATRACE_CALL();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerHintSession::HintTimerHandler::handleMessage(const Message &) {
|
||||||
std::lock_guard<std::mutex> guard(mStaleLock);
|
std::lock_guard<std::mutex> guard(mStaleLock);
|
||||||
if (mIsSessionDead) {
|
if (mIsSessionDead) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
auto when = getStaleTime();
|
auto staleTime = getStaleTime();
|
||||||
// Check if the session is stale based on the last_updated_time.
|
auto earlyBoostTime = getEarlyBoostTime();
|
||||||
if (now > when) {
|
if (adpfConfig->mEarlyBoostOn && now < earlyBoostTime) {
|
||||||
|
int64_t next =
|
||||||
|
static_cast<int64_t>(duration_cast<nanoseconds>(earlyBoostTime - now).count());
|
||||||
|
mState = MONITORING;
|
||||||
|
// Schedule for the early hint check.
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
||||||
|
next, mSession->mHintTimerHandler, NULL);
|
||||||
|
if (ATRACE_ENABLED()) {
|
||||||
|
const std::string idstr = mSession->getIdString();
|
||||||
|
std::string sz = StringPrintf("adpf.%s-timer.nexthint", idstr.c_str());
|
||||||
|
ATRACE_INT(sz.c_str(), next);
|
||||||
|
}
|
||||||
|
} else if (now >= staleTime) { // Check if the session is stale.
|
||||||
mSession->setStale();
|
mSession->setStale();
|
||||||
mIsMonitoringStale.store(false);
|
mState = STALE;
|
||||||
return;
|
} else { // Check if it's time to do early boost.
|
||||||
|
if (adpfConfig->mEarlyBoostOn) {
|
||||||
|
mState = EARLY_BOOST;
|
||||||
|
mSession->setUclamp(adpfConfig->mUclampMinHigh);
|
||||||
|
}
|
||||||
|
int64_t next = static_cast<int64_t>(duration_cast<nanoseconds>(staleTime - now).count());
|
||||||
|
// Schedule for the stale timeout check.
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
|
||||||
|
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
||||||
|
next, mSession->mHintTimerHandler, NULL);
|
||||||
|
}
|
||||||
|
if (ATRACE_ENABLED()) {
|
||||||
|
const std::string idstr = mSession->getIdString();
|
||||||
|
std::string sz = StringPrintf("adpf.%s-timer.state", idstr.c_str());
|
||||||
|
ATRACE_INT(sz.c_str(), mState);
|
||||||
}
|
}
|
||||||
// Schedule for the next checking time.
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleHandler);
|
|
||||||
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
|
|
||||||
duration_cast<nanoseconds>(when - now).count(), mSession->mStaleHandler, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerHintSession::StaleHandler::setSessionDead() {
|
void PowerHintSession::HintTimerHandler::setSessionDead() {
|
||||||
std::lock_guard<std::mutex> guard(mStaleLock);
|
std::lock_guard<std::mutex> guard(mStaleLock);
|
||||||
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mStaleHandler);
|
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
|
||||||
mIsSessionDead = true;
|
mIsSessionDead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,9 @@ struct AppHintDesc {
|
||||||
is_active(true),
|
is_active(true),
|
||||||
update_count(0),
|
update_count(0),
|
||||||
integral_error(0),
|
integral_error(0),
|
||||||
previous_error(0) {}
|
previous_error(0),
|
||||||
|
work_period(0),
|
||||||
|
last_start(0) {}
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
const int32_t tgid;
|
const int32_t tgid;
|
||||||
const int32_t uid;
|
const int32_t uid;
|
||||||
|
@ -66,6 +68,9 @@ struct AppHintDesc {
|
||||||
uint64_t update_count;
|
uint64_t update_count;
|
||||||
int64_t integral_error;
|
int64_t integral_error;
|
||||||
int64_t previous_error;
|
int64_t previous_error;
|
||||||
|
// earlyhint pace
|
||||||
|
int64_t work_period;
|
||||||
|
int64_t last_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PowerHintSession : public BnPowerHintSession {
|
class PowerHintSession : public BnPowerHintSession {
|
||||||
|
@ -85,22 +90,34 @@ class PowerHintSession : public BnPowerHintSession {
|
||||||
int restoreUclamp();
|
int restoreUclamp();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class StaleHandler : public MessageHandler {
|
class HintTimerHandler : public MessageHandler {
|
||||||
public:
|
public:
|
||||||
StaleHandler(PowerHintSession *session)
|
enum HintTimerState {
|
||||||
|
STALE,
|
||||||
|
MONITORING,
|
||||||
|
EARLY_BOOST,
|
||||||
|
};
|
||||||
|
HintTimerHandler(PowerHintSession *session)
|
||||||
: mSession(session),
|
: mSession(session),
|
||||||
mIsMonitoringStale(false),
|
mState(STALE),
|
||||||
mLastUpdatedTime(steady_clock::now()),
|
mLastUpdatedTime(steady_clock::now()),
|
||||||
mIsSessionDead(false) {}
|
mIsSessionDead(false) {}
|
||||||
|
~HintTimerHandler();
|
||||||
void handleMessage(const Message &message) override;
|
void handleMessage(const Message &message) override;
|
||||||
void updateStaleTimer();
|
// Update HintTimer by actual work duration.
|
||||||
|
void updateHintTimer(int64_t actualDurationNs);
|
||||||
|
// Update HintTimer by a list of work durations which could be used for
|
||||||
|
// calculating the work period.
|
||||||
|
void updateHintTimer(const std::vector<WorkDuration> &actualDurations);
|
||||||
|
time_point<steady_clock> getEarlyBoostTime();
|
||||||
time_point<steady_clock> getStaleTime();
|
time_point<steady_clock> getStaleTime();
|
||||||
void setSessionDead();
|
void setSessionDead();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PowerHintSession *mSession;
|
PowerHintSession *mSession;
|
||||||
std::atomic<bool> mIsMonitoringStale;
|
HintTimerState mState;
|
||||||
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
|
std::atomic<time_point<steady_clock>> mLastUpdatedTime;
|
||||||
|
std::atomic<time_point<steady_clock>> mNextStartTime;
|
||||||
std::mutex mStaleLock;
|
std::mutex mStaleLock;
|
||||||
bool mIsSessionDead;
|
bool mIsSessionDead;
|
||||||
};
|
};
|
||||||
|
@ -111,7 +128,7 @@ class PowerHintSession : public BnPowerHintSession {
|
||||||
int setUclamp(int32_t min, bool update = true);
|
int setUclamp(int32_t min, bool update = true);
|
||||||
std::string getIdString() const;
|
std::string getIdString() const;
|
||||||
AppHintDesc *mDescriptor = nullptr;
|
AppHintDesc *mDescriptor = nullptr;
|
||||||
sp<StaleHandler> mStaleHandler;
|
sp<HintTimerHandler> mHintTimerHandler;
|
||||||
sp<MessageHandler> mPowerManagerHandler;
|
sp<MessageHandler> mPowerManagerHandler;
|
||||||
std::mutex mLock;
|
std::mutex mLock;
|
||||||
std::atomic<bool> mSessionClosed = false;
|
std::atomic<bool> mSessionClosed = false;
|
||||||
|
|
Loading…
Reference in a new issue