#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // This is sort of a nice idea, but also a bit cumbersome to use // // .NET task use this concept, but the only advantage this gives is that lambdas and // any random junk can be executed as a task ad-hoc without having to inherit from // a task base class to support cancellation. // // I think requiring classes to be types gives more advantages than externalizing // the concept of cancellation. For ad-hoc junk, you can still make a task that ignores // cancellation (or passes it to the ad-hoc junk) and wrap that. // #ifndef NUCLEX_PLATFORM_TASKS_CANCELLATIONTRIGGER_H #define NUCLEX_PLATFORM_TASKS_CANCELLATIONTRIGGER_H #include "Nuclex/Platform/Config.h" #include "Nuclex/Platform/Tasks/CancellationWatcher.h" #include // for assert() namespace Nuclex { namespace Platform { namespace Tasks { // ------------------------------------------------------------------------------------------- // /// Allows cancelling all tasks holding the trigger's cancellation watcher /// /// /// This is very similar to Microsoft's concept of "cancellation tokens" /// found in their PPL, C++ REST SDK and in .NET /// /// /// Basically, the initial launcher of a background task provides the task with /// a cancellation watcher (as a parameter to the initiating method). The task is /// then supposed to hold onto the cancellation watcher and stop running when /// the cancellation watcher's /// property is set to true (by sporadically checking it). /// /// class NUCLEX_PLATFORM_TYPE CancellationTrigger : protected CancellationWatcher { /// /// Builds a new cancellation trigger, required to prevent stack allocations /// /// The new cancellation trigger public: NUCLEX_PLATFORM_API static std::shared_ptr Create() { struct ConstructibleCancellationTrigger : public CancellationTrigger { ConstructibleCancellationTrigger() = default; virtual ~ConstructibleCancellationTrigger() override = default; }; std::shared_ptr result( std::make_shared() ); #if 0 // This seems to be a spot where compilers may report an error. // The reason being that, unlike the CancellationTrigger::Create() method, // std::static_pointer_cast() does not have access to protected members of // this class, and that includes the protected base class, unfortunately. result->watcher = ( std::static_pointer_cast(result) ); #else // This is pure shared_ptr villainy, but for single-inheritance, // the pointer to base and derived will be at the same address // on all supported compilers, so we can do this and avoid having // to declare the base class as public (which would suck!) // // Make a weak_ptr to a CancellationWatcher that's actually us in disguise result->watcher = *reinterpret_cast *>(&result); #endif return result; } /// Initializes a new cancellation trigger protected: NUCLEX_PLATFORM_API CancellationTrigger() = default; /// Frees all resources used by the cancellation trigger public: NUCLEX_PLATFORM_API virtual ~CancellationTrigger() override = default; /// Returns the trigger's cancellation watcher /// The cancellation watcher responding to the trigger public: NUCLEX_PLATFORM_API std::shared_ptr GetWatcher() const { return this->watcher.lock(); } /// Triggers the cancellation, signaling the watcher /// /// Optional reason for the cancellation, included in exception message when /// is used. /// public: NUCLEX_PLATFORM_API void Cancel(const std::string &reason = std::string()) { assert((IsCanceled() == false) && u8"Cancellation is triggered only once"); this->CancellationReason = reason; std::atomic_thread_fence(std::memory_order::memory_order_release); this->Canceled.store(true, std::memory_order::memory_order_relaxed); } /// Watcher handed out by the method /// /// This is actually a weak_ptr to this instance itself under its base class. /// It cannot be a shared_ptr since then, the instance would self-reference, /// preventing the reference counter from ever reaching zero, causing a memory leak. /// private: std::weak_ptr watcher; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Platform::Tasks #endif // NUCLEX_PLATFORM_TASKS_CANCELLATIONTRIGGER_H