diff --git a/relnotes/verify.feature.md b/relnotes/verify.feature.md new file mode 100644 index 000000000..6d6aedcbc --- /dev/null +++ b/relnotes/verify.feature.md @@ -0,0 +1,9 @@ +* `ocean.core.Verify` + + New module with a single function, `verify`, intended to serve as drop-in + replacement for `assert` to comply to [Sociomantic assert/enforce + policies](https://github.com/sociomantic-tsunami/sociomantic/blob/master/Code/assert-vs-enforce.rst) + + It works similar to `enforce` as it throws `Exception` instead of an `Error` + and will remain even when built with `-release`. But it also uses specific + pre-constructed `SanityException` type to indicate importance of the failure. diff --git a/src/ocean/core/Verify.d b/src/ocean/core/Verify.d new file mode 100644 index 000000000..3a6dac6bb --- /dev/null +++ b/src/ocean/core/Verify.d @@ -0,0 +1,79 @@ +/******************************************************************************* + + Utility intended as a replacement for `assert` to check for programming + errors and sanity violations in situations when neither removing the check + in -release mode nor bringing down the application by throwing an `Error` + is acceptable. + + This module must have as few import dependencies as possible so that it can + be used in place of `assert` freely without introducing cyclic imports. + + Copyright: Copyright (c) 2017 sociomantic labs GmbH. All rights reserved + + License: + Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. + Alternatively, this file may be distributed under the terms of the Tango + 3-Clause BSD License (see LICENSE_BSD.txt for details). + +*******************************************************************************/ + +module ocean.core.Verify; + +import ocean.meta.types.Qualifiers : istring; + +/******************************************************************************* + + Verifies that certain condition is met. + + Params: + ok = boolean condition to check + msg = optional exception message + + Throws: + SanityException if `ok` condition is `false`. + +*******************************************************************************/ + +public void verify ( bool ok, lazy istring msg = "", + istring file = __FILE__, int line = __LINE__ ) +{ + static SanityException exc; + + if (exc is null) + exc = new SanityException(""); + + if (!ok) + { + exc.file = file; + exc.line = line; + exc.msg = msg; + + throw exc; + } +} + +unittest +{ + try + { + verify(false); + } + catch (SanityException e) { } + + verify(true); +} + +/******************************************************************************* + + Indicates some internal sanity violation in the app, essentially a less + fatal version of `AssertError`. + +*******************************************************************************/ + +public class SanityException : Exception +{ + public this ( istring msg, istring file = __FILE__, int line = __LINE__ ) + { + super(msg, file, line); + } +} diff --git a/src/ocean/task/Scheduler.d b/src/ocean/task/Scheduler.d index 10973a2d6..17a7a4a55 100644 --- a/src/ocean/task/Scheduler.d +++ b/src/ocean/task/Scheduler.d @@ -30,6 +30,7 @@ import core.thread; import ocean.transition; import ocean.core.Enforce; +import ocean.core.Verify; import ocean.core.Traits; import ocean.core.array.Mutation : reverse; import ocean.io.select.EpollSelectDispatcher; @@ -130,16 +131,6 @@ final class Scheduler /// ditto private State state = State.Initial; - /*************************************************************************** - - Thrown instead of `AssertError` when scheduler sanity is violated. Such - issues are almost impossible to reason about if not caught in time - thus we want all sanity checks to remain even in `-release` mode. - - ***************************************************************************/ - - private SchedulerSanityException sanity_e; - /*************************************************************************** Thrown when all fibers are busy, task queue is full and no custom @@ -278,12 +269,10 @@ final class Scheduler config.suspended_task_limit ); - this.sanity_e = new SchedulerSanityException; this.queue_full_e = new TaskQueueFullException; this.suspend_queue_full_e = new SuspendQueueFullException; - enforce( - this.sanity_e, + verify( config.task_queue_limit >= config.worker_fiber_limit, "Must configure task queue size at least equal to worker fiber " ~ "count for optimal task scheduler performance." @@ -569,7 +558,7 @@ final class Scheduler as there is at least one event registered. Throws: - SchedulerSanityException if there are some active worker fibers + SanityException if there are some active worker fibers left in the pool by the time there are not events left ***************************************************************************/ @@ -602,9 +591,9 @@ final class Scheduler foreach (ref fiber; iterator) fiber.active_task.kill(); - enforce(this.sanity_e, this.fiber_pool.num_busy() == 0); - enforce(this.sanity_e, this.queued_tasks.length() == 0); - enforce(this.sanity_e, this.suspended_tasks.length() == 0); + verify(this.fiber_pool.num_busy() == 0); + verify(this.queued_tasks.length() == 0); + verify(this.suspended_tasks.length() == 0); } /*************************************************************************** @@ -645,7 +634,7 @@ final class Scheduler task = task to run Throws: - SchedulerSanityException on attempt to run new task from the very + SanityException on attempt to run new task from the very same fiber which would result in fiber resetting own state. ***************************************************************************/ @@ -751,7 +740,7 @@ final class Scheduler for (auto i = 0; i < current_count; ++i) { Task task; - enforce(this.sanity_e, this.suspended_tasks.pop(task)); + verify(this.suspended_tasks.pop(task)); if (this.state == State.Shutdown) task.kill(); else @@ -1011,21 +1000,6 @@ public class SuspendQueueFullException : Exception } } -/****************************************************************************** - - Exception class that indicates scheduler internal sanity violation, - for example, worker fiber leak. - -******************************************************************************/ - -private class SchedulerSanityException : Exception -{ - this ( ) - { - super("Internal sanity violation using the scheduler"); - } -} - private void debug_trace ( T... ) ( cstring format, T args ) { debug ( TaskScheduler ) diff --git a/src/ocean/task/internal/FiberPool.d b/src/ocean/task/internal/FiberPool.d index ab3fb9b57..f97872e55 100644 --- a/src/ocean/task/internal/FiberPool.d +++ b/src/ocean/task/internal/FiberPool.d @@ -24,7 +24,7 @@ module ocean.task.internal.FiberPool; import core.thread; import ocean.task.Task; -import ocean.core.Enforce; +import ocean.core.Verify; import ocean.util.container.pool.ObjectPool; debug (TaskScheduler) @@ -51,15 +51,6 @@ public class FiberPool : ObjectPool!(WorkerFiber) private size_t stack_size; - /************************************************************************** - - Exception object thrown if internal pool state is compromised, should - normally result in application termination. - - **************************************************************************/ - - private FiberPoolSanityException exception; - /************************************************************************** Constructor @@ -75,7 +66,6 @@ public class FiberPool : ObjectPool!(WorkerFiber) this ( size_t stack_size, size_t limit = 0 ) { this.stack_size = stack_size; - this.exception = new FiberPoolSanityException; if (limit > 0) this.setLimit(limit); } @@ -101,7 +91,7 @@ public class FiberPool : ObjectPool!(WorkerFiber) auto fiber = super.get(if_missing); if (fiber is null) return null; - enforce(this.exception, fiber.state() != Fiber.State.EXEC); + verify(fiber.state() != Fiber.State.EXEC); return fiber; } @@ -136,25 +126,9 @@ public class FiberPool : ObjectPool!(WorkerFiber) override protected void resetItem ( Item item ) { auto fiber = this.fromItem(item); - enforce( - this.exception, + verify( fiber is Fiber.getThis() || fiber.state() == Fiber.State.TERM ); } } - -/****************************************************************************** - - Exception class that indicates fiber pool internal sanity violation, - for example, trying to recycle fiber that is still running. - -******************************************************************************/ - -private class FiberPoolSanityException : Exception -{ - this ( ) - { - super("Internal sanity violation using fiber pool"); - } -}