| Index: src/Timeout.h |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/src/Timeout.h |
| @@ -0,0 +1,303 @@ |
| +/* |
| + * This file is part of Adblock Plus <https://adblockplus.org/>, |
| + * Copyright (C) 2006-2017 Eyeo GmbH |
| + * |
| + * Adblock Plus is free software: you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 3 as |
| + * published by the Free Software Foundation. |
| + * |
| + * Adblock Plus 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 |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| + */ |
| + |
| +#if !defined(TIMEOUT_H) |
| +#define TIMEOUT_H |
| + |
| +#include <chrono> |
| +#include <condition_variable> |
| +#include <mutex> |
| +#include <v8.h> |
| + |
| +/** |
| + * Callback function implementing "SetTimeout()" on the global JS object. |
| + */ |
| +v8::Handle<v8::Value> CallbackForSetTimeout(const v8::Arguments& arguments); |
| + |
| +/** |
| + * Base class for aspect classes. |
| + * Provides uniform accessor to the aspect. |
| + * Instantiate using CRTP. |
| + */ |
| +template<class AspectType> |
| +class AspectBase |
| +{ |
| +public: |
| + AspectType& Aspect() |
| + { |
| + return *static_cast<AspectType*>(this); |
| + } |
| +}; |
| + |
| +/** |
| + * Definitions for all aspect points used in DelayedExecutorAspected. |
| + */ |
| +struct DelayedExecutorAspectPoints |
| +{ |
| + template<class LockType> |
| + void RunnableToSleeping(LockType& lock) {}; |
| + |
| + void RunFinish() {}; |
| +}; |
| + |
| +class DelayedExecutorNullAspect |
| + : public AspectBase<DelayedExecutorNullAspect>, |
| + public DelayedExecutorAspectPoints |
| +{}; |
| + |
| +/** |
| + * Execute a function after a certain time has elapsed. |
| + * Or don't execute it if you change your mind beforehand. |
| + * |
| + * This is the functional equivalent of sleeping-then-executing, |
| + * with the exception that we can wake up the sleep early and stop execution. |
| + */ |
| +template<class AspectType> |
| +class DelayedExecutorAspected |
| + : protected AspectType |
| +{ |
| + enum State |
| + { |
| + Runnable, ///< The state at construction. Awaiting a command to run. |
| + Sleeping, ///< Ordered to run, but the delay timer is still ticking. |
| + Interrupted, ///< Sleep was interrupted |
| + Running, ///< In the middle of execution. |
| + Completed, ///< Execution completed. |
| + Error ///< An error encountered anywhere. Disables all operations. |
| + }; |
| + |
| + /** |
| + * Internal state. |
| + * |
| + * \par State Machine |
| + * - Initial state is always "Runnable". |
| + * - Final states are "Completed" and "Error". |
| + * - Every non-final state may transition to "Error". |
| + * - The ordinary linear sequence: |
| + * "Runnable" --> "Sleeping" --> "Running" --> "Completed". |
| + * - Accelerated execution: |
| + * "Runnable" --> "Running" |
| + * - The interruption transitions: |
| + * "Sleeping" --> "Interrupted" --> "Completed" |
| + */ |
| + State state; |
| + |
| + /** |
| + * Mutex primarily protects the internal state machine. |
| + */ |
| + std::mutex m; |
| + /** |
| + * Condition variable to wait on instead of sleeping. |
| + */ |
| + std::condition_variable cv; |
| + /** |
| + * Lock type is unique for use with condition variable. |
| + */ |
| + typedef std::unique_lock<std::mutex> LockType; |
| + /** |
| + * The function that may execute, or not. |
| + */ |
| + std::function<void()> body; |
| + |
| + /** |
| + * Validation routine. |
| + */ |
| + void AssertNotInErrorState() |
| + { |
| + if (state == Error) |
| + { |
| + throw std::runtime_error("May not perform operation in error state"); |
| + } |
| + } |
| + |
| + /** |
| + * Run the body unconditionally. |
| + * This function does not take responsibility for prior state validation. |
| + */ |
| + void Run() |
| + { |
| + try |
| + { |
| + // Run body with mutex unlocked |
| + body(); |
| + // Now that body has completed successfully we can change state |
| + LockType lock(m); |
| + state = Completed; |
| + } |
| + catch (...) |
| + { |
| + LockType lock(m); |
| + state = Error; |
| + } |
| + Aspect().RunFinish(); |
| + } |
| + |
| +public: |
| + /** |
| + * Constructor starts the delay timer. |
| + * If the delay is zero, execution begins immediately. |
| + * |
| + * @param body |
| + * A function object to execute. May not be empty. |
| + */ |
| + DelayedExecutorAspected(AspectType&& aspect, std::function<void()> body) |
| + : AspectType(std::move(aspect)), state(Runnable), body(body) |
| + { |
| + if (!body) |
| + { |
| + throw std::logic_error("Body function may not be empty"); |
| + } |
| + } |
| + |
| + /** |
| + * Run the function body immediately and block until it's done. |
| + * |
| + * If we are not runnable, this is an error. |
| + */ |
| + void RunImmediately() |
| + { |
| + // Validate state and transition with mutex protection |
| + { |
| + LockType lock(m); |
| + switch (state) |
| + { |
| + case Runnable: |
| + case Sleeping: |
| + state = Running; |
| + break; |
| + case Interrupted: |
| + case Running: |
| + case Completed: |
| + throw std::runtime_error("May not run immediately unless state is runnable or sleeping"); |
| + case Error: |
| + throw std::runtime_error("May not run immediately from the error state"); |
| + default: |
| + throw std::logic_error("Unknown state"); |
| + } |
| + } |
| + // Run with mutex unlocked |
| + Run(); |
| + } |
| + |
| + /** |
| + * Run the body after some period of time has elapsed. |
| + */ |
| + template <class Rep, class Period> |
| + void RunWithDelay(const std::chrono::duration<Rep, Period>& duration) |
| + { |
| + // Validate state and transition with mutex protection |
| + { |
| + LockType lock(m); |
| + switch (state) |
| + { |
| + case Runnable: |
| + state = Sleeping; |
| + break; |
| + case Sleeping: |
| + case Interrupted: |
| + case Running: |
| + case Completed: |
| + throw std::runtime_error("May not run with delay unless state is runnable"); |
| + case Error: |
| + throw std::runtime_error("May not run with delay from the error state"); |
| + default: |
| + throw std::logic_error("Unknown state"); |
| + } |
| + |
| + /* |
| + * Aspect point for beginning of sleep. |
| + * Ideally, this point would be inside the condition variable as it |
| + * transitions to waiting, immediately after it unlocks its mutex. |
| + * Putting it right before the statement is a reasonable substitute. |
| + * Note: aspect should unlock and re-lock mutex |
| + */ |
| + Aspect().RunnableToSleeping(lock); |
| + |
| + auto completed = [this]() -> bool |
| + { |
| + return state != Sleeping; |
| + }; |
| + // Assert state == Sleeping |
| + cv.wait_for(lock, duration, completed); |
| + // Assert either the wait timed out or we are no longer in the sleeping state |
| + |
| + switch (state) |
| + { |
| + case Sleeping: |
| + // We timed out and sleep was completed. Time to run the body. |
| + state = Running; |
| + break; |
| + case Interrupted: |
| + // We were interrupted while sleeping, so we're all done. |
| + state = Completed; |
| + return; |
| + default: |
| + throw std::runtime_error("Unexpected state after finished sleeping"); |
| + } |
| + } |
| + // Assert state == Running |
| + Run(); |
| + } |
| + |
| + /** |
| + * Stave off running our function body if that's still possible. |
| + * |
| + * If we are "Running", return false. |
| + * If we are "Runnable" or "Sleeping", then we transition to "Completed" |
| + * and return true; in this case the body never executes. |
| + * If we are "Completed", we simply return true. |
| + * |
| + * @return |
| + * - True if we have verified that execution has completed. |
| + * - False if we cannot verify that execution has completed. |
| + * - Note that we can return false here but execution can complete on its |
| + * own before we receive our next call. |
| + */ |
| + bool ForestallExecution() |
| + { |
| + LockType lock(m); |
| + switch (state) |
| + { |
| + case Runnable: |
| + case Sleeping: |
| + state = Interrupted; |
| + // fall through |
| + case Interrupted: // permissible, but usually irrelevant |
| + cv.notify_one(); |
| + return true; |
| + case Running: |
| + case Completed: |
| + return false; |
| + case Error: |
| + throw std::runtime_error("May not forestall execution from the error state"); |
| + default: |
| + throw std::logic_error("Unknown state"); |
| + } |
| + } |
| +}; |
| + |
| +class DelayedExecutor |
| + : public DelayedExecutorAspected<DelayedExecutorNullAspect> |
| +{ |
| +public: |
| + typedef DelayedExecutorAspected<DelayedExecutorNullAspect> Base; |
| + DelayedExecutor(std::function<void()> body) |
| + : Base(DelayedExecutorNullAspect(), body) |
| + {} |
| +}; |
| + |
| +#endif |