-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Defuse throttled_func when it's accidentally engaged #18235
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,12 +30,22 @@ namespace til | |
} | ||
} | ||
|
||
std::tuple<Args...> take() | ||
void apply(const auto& func) | ||
{ | ||
std::unique_lock guard{ _lock }; | ||
auto pendingRunArgs = std::move(*_pendingRunArgs); | ||
_pendingRunArgs.reset(); | ||
return pendingRunArgs; | ||
decltype(_pendingRunArgs) args; | ||
{ | ||
std::unique_lock guard{ _lock }; | ||
args = std::exchange(_pendingRunArgs, std::nullopt); | ||
} | ||
// Theoretically it should always have a value, because the throttled_func | ||
// should not call the callback without there being a reason. | ||
// But in practice a failure here was observed at least once. | ||
// It's unknown to me what caused it, so the best we can do is avoid a crash. | ||
assert(args.has_value()); | ||
if (args) | ||
{ | ||
std::apply(func, *args); | ||
} | ||
} | ||
|
||
explicit operator bool() const | ||
|
@@ -60,10 +70,12 @@ namespace til | |
return _isPending.exchange(true, std::memory_order_relaxed); | ||
} | ||
|
||
std::tuple<> take() | ||
void apply(const auto& func) | ||
{ | ||
reset(); | ||
return {}; | ||
if (_isPending.exchange(false, std::memory_order_relaxed)) | ||
{ | ||
func(); | ||
} | ||
} | ||
|
||
void reset() | ||
|
@@ -171,31 +183,24 @@ namespace til | |
void flush() | ||
{ | ||
WaitForThreadpoolTimerCallbacks(_timer.get(), true); | ||
if (_storage) | ||
{ | ||
_trailing_edge(); | ||
} | ||
_timer_callback(nullptr, this, nullptr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading this code I've realized that I can ever so slightly reduce the binary size by moving There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends honestly. If all of timer_callback is larger and less deduplicatable with COMDAT folding, it could be larger overall. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I think I see what you mean. It does have the additional benefit that it swallows the exception, just like the regular timer callback. This makes it more predictable IMO, because it now behaves identical. |
||
} | ||
|
||
private: | ||
static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept | ||
try | ||
{ | ||
static_cast<throttled_func*>(context)->_trailing_edge(); | ||
} | ||
CATCH_LOG() | ||
|
||
void _trailing_edge() | ||
{ | ||
const auto self = static_cast<throttled_func*>(context); | ||
if constexpr (Leading) | ||
{ | ||
_storage.reset(); | ||
self->_storage.reset(); | ||
} | ||
else | ||
{ | ||
std::apply(_func, _storage.take()); | ||
self->_storage.apply(self->_func); | ||
} | ||
} | ||
CATCH_LOG() | ||
|
||
wil::unique_threadpool_timer _createTimer() | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested by Dustin, this now uses
std::exchange
.