Files
libspinscale/include/spinscale/syncCancelerForAsyncWork.h
T
hayodea 4dbc066aac New class: SyncCancelerForAsyncWork
This class abstracts the pattern of running an async callee which
needs to be able to be canceled from a synchronous code path.

It really just lifts the logic that's regularly used in the
StimulusProducer path into a reusable abstraction.
2026-05-29 06:22:02 -04:00

86 lines
2.2 KiB
C++

#ifndef SYNC_CANCELER_FOR_ASYNC_WORK_H
#define SYNC_CANCELER_FOR_ASYNC_WORK_H
#include <concepts>
#include <utility>
#include <spinscale/sharedResourceGroup.h>
#include <spinscale/spinLock.h>
namespace sscl {
/**
* SyncCancelerForAsyncWork
*
* A small helper to coordinate synchronous cancellation requests with
* asynchronous work that must only observe cancellation at explicit
* uncancelable segment boundaries.
*
* The async callee should structure its logic as:
* - enter an uncancelable segment (execUncancelableSegmentOrAbort)
* - perform synchronous work that must not be interrupted
* - exit the segment
* - perform cancelable async work (outside the lock)
* - repeat
*
* requestStop() blocks until any currently-executing segment releases s.lock,
* then flips shouldContinue to false. This guarantees shouldContinue is stable
* throughout each uncancelable segment.
*
* startAcceptingWork() is intentionally unlocked. Precondition: callers must
* only call startAcceptingWork() when no async callee is running yet (e.g. at
* the end of setup(), before posting/arming the first async work). If this
* method races a running callee, that is a caller bug.
*/
class SyncCancelerForAsyncWork
{
public:
SyncCancelerForAsyncWork() = default;
void startAcceptingWork()
{
// Intentionally unlocked — see class-level EXPLANATION above.
s.rsrc.shouldContinue = true;
}
/** @return shouldContinue before this call (was work being accepted?) */
bool requestStop()
{
sscl::SpinLock::Guard guard(s.lock);
const bool wasContinuing = s.rsrc.shouldContinue;
s.rsrc.shouldContinue = false;
return wasContinuing;
}
/** @return true if requestStop() has set shouldContinue to false. */
bool isCancellationRequested()
{
sscl::SpinLock::Guard guard(s.lock);
return !s.rsrc.shouldContinue;
}
template<typename Body>
requires std::invocable<Body>
bool execUncancelableSegmentOrAbort(Body&& body)
{
sscl::SpinLock::Guard guard(s.lock);
if (!s.rsrc.shouldContinue) {
return false;
}
std::forward<Body>(body)();
return true;
}
private:
struct Resources
{
bool shouldContinue = false;
};
sscl::SharedResourceGroup<sscl::SpinLock, Resources> s;
};
} // namespace sscl
#endif // SYNC_CANCELER_FOR_ASYNC_WORK_H