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 |