Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace assertions with verifies in ocean.io.* #251

Conversation

nemanja-boric-sociomantic
Copy link
Contributor

No description provided.

import ocean.core.MessageFiber;

import ocean.core.Verify;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double import

@@ -383,10 +384,10 @@ class FileSystemEvent : ISelectClient
{
foreach ( ev; this.fd.readEvents() )
{
assert(ev.mask);
verify(ev.mask != 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably would be better off with ev.mask != ev.mask.init

@@ -251,6 +248,7 @@ public class SelectEvent : ISelectEvent

protected override bool handle_ ( ulong n )
{
verify(this.handler !is null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is added here, because it was in line 147, but never compiled (in contracts of the abstract method's implementations are not compiled).

@@ -450,7 +452,7 @@ class DataOutput : OutputFilter
private final void eat (Const!(void)* src, size_t bytes)
{
auto count = output.write (src[0..bytes]);
assert (count is bytes);
verify(count == bytes);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any difference in using is vs == for value types?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For structs only (as they may provide own opEqual)

@codecov
Copy link

codecov bot commented Sep 14, 2017

Codecov Report

Merging #251 into v3.x.x will decrease coverage by 0.06%.
The diff coverage is 26.29%.

@@            Coverage Diff             @@
##           v3.x.x     #251      +/-   ##
==========================================
- Coverage   71.96%   71.89%   -0.07%     
==========================================
  Files         458      458              
  Lines       38798    38836      +38     
==========================================
+ Hits        27922    27923       +1     
- Misses      10876    10913      +37

@@ -58,6 +60,7 @@ package import ocean.io.device.Conduit : InputFilter, InputBuffer, InputStream;

class Iterator(T) : InputFilter
{
import ocean.core.Verify;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double import

Copy link

@gavin-norman-sociomantic gavin-norman-sociomantic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All looks ok to me. I agree with your comments.

@@ -161,9 +160,9 @@ class TaskSelectClient: ISelectClient
try
{
this.task = task.getThis();
assert(this.task);
verify(this.task !is null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cases like this we have to be careful: verify(object !is null) does not call the object invariant while assert(object) does. Task doesn't have an invariant so it's fine here, but in general this change can silently drop a debugging aid.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the better thing to do would be:

// old
assert(object);

// replace with
verify(object !is null && object); // call invariant

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only literally assert(object) works. It is a magic expression to call the invariant.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And assert(task) is perfectly fine here:

  1. it will just crash on the next line in release build
  2. it protects against being called from main thread context - not something that may not be noticed before production deloyment

@@ -206,7 +200,7 @@ struct StructSerializer ( bool AllowUnions = false )
size_t transmit ( bool receive, S ) ( S* s, void delegate ( void[] data ) transmit_data )
in
{
assert (s, typeof (*this).stringof ~ ".transmit (receive = " ~
verify(s, typeof (*this).stringof ~ ".transmit (receive = " ~
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be s !is null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an in contract here anyway :-)

@@ -1460,11 +1462,11 @@ in
switch (pattern[i])
{
case '[':
assert(!inbracket);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've grepped for in$ and this showed up as a remaining in contract. This feels very expensive to be left in release build. Should I just leave it here and use assert?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is patternMatch a function that you could imagine being called in time critical code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puh, I guess you're right. It's still O(n), but let's KISS.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep it nested inside {} block to make it easier to eventually reconsider and remove it if needed

@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated to address @david-eckardt-sociomantic 's comment and to move one debug block from in contract (it couldn't fail, it was just printout) to the body.

@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some arguable comments from my side :)

{
verify(dst !is null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verify(dst.length > 0) makes more sense here

@@ -1460,11 +1462,11 @@ in
switch (pattern[i])
{
case '[':
assert(!inbracket);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep it nested inside {} block to make it easier to eventually reconsider and remove it if needed

@@ -33,6 +33,7 @@ module ocean.io.model.SuspendableThrottlerCount;
*******************************************************************************/

import ocean.transition;
import ocean.core.Verify;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all checks in this module actually should remain assert - they don't protect from any kind of memory corruption and relate to hot path in apps.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they don't protect from any kind of memory corruption

But they are valid sanity checks, which is the purpose of verify. I don't see any harm leaving them as verify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I think as well. Perhaps the trick is "hot path"?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, purpose of verify is specifically sanity checking you can not abandon. So that when you build -release you get maximum performance possible while still not compromising the app. No more, no less.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, "sanity check that is nice to have to catch weird app state but which won't help to keep the app working and won't prevent anything bad" is pretty much definition of assert use case.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh... this makes it a very tricky thing to decide, in many cases. A bug in this class, for example, could lead to seriously wrong app behaviour. Can those checks be abandoned?

Also: I was viewing this conversion process as allowing us to run release builds of D2 apps without losing any of the sanity checks that are in place right now but also without the risk of Errors being thrown. This is a somewhat different (and I would argue far more pressing) goal.

@@ -300,8 +302,8 @@ public abstract class EpollProcess

if (pid == -1)
{
assert( errno() == ECHILD );
assert( this.processes.length == 0 );
verify(errno() == ECHILD);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either assert or enforce - either this is protecting from OS being broken or checking external state that is beyond programmer control

{
verify(super.fiber.waiting);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert is fine, fiber will have own verify on resume

@@ -161,9 +160,9 @@ class TaskSelectClient: ISelectClient
try
{
this.task = task.getThis();
assert(this.task);
verify(this.task !is null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And assert(task) is perfectly fine here:

  1. it will just crash on the next line in release build
  2. it protects against being called from main thread context - not something that may not be noticed before production deloyment

this.task.suspend();
assert(!this.task);
verify(this.task is null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can be assert

@@ -219,7 +220,7 @@ public class StringStructSerializer ( Char )

public void open ( ref Char[] output, cstring name )
{
assert(this.indent.length == 0, "Non-zero indentation in open");
verify(this.indent.length == 0, "Non-zero indentation in open");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Error message is reverse wrong btw.
  2. I think all those check must be assert (probably with reusable exception even) as serialization often happens with I/O data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look that the error message is wrong :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So many negatives :grumpy:

{
verifyStructPtr!("dump")(s);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this module - it all should enforce with actual exception, those are not sanity violations

@@ -450,7 +452,7 @@ class DataOutput : OutputFilter
private final void eat (Const!(void)* src, size_t bytes)
{
auto count = output.write (src[0..bytes]);
assert (count is bytes);
verify(count == bytes);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For structs only (as they may provide own opEqual)

@mihails-strasuns-sociomantic
Copy link
Contributor

With last discussion in mind only remaining thing I'd like to happen is changing verify to enforce in serializer

@nemanja-boric-sociomantic
Copy link
Contributor Author

I'll change them 👍

@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated with the enforcements for the StringStructSerializer and StructSerializer.

@mihails-strasuns-sociomantic
Copy link
Contributor

Red

./src/ocean/io/serialize/StructSerializer.d(64): Error: constructor ocean.io.serialize.StructSerializer.SerializerException.ReusableExceptionImplementation!().this (ulong size = cast(ulong)128) does not match parameter types (char[],char[],int)
./src/ocean/io/serialize/StructSerializer.d(64): Error: expected 1 arguments, not 3 for non-variadic function type SerializerException(ulong size = cast(ulong)128)
./src/ocean/io/serialize/StructSerializer.d(64): Error: cannot implicitly convert expression (msg) of type char[] to ulong

Most serialization is being done with the external data, so in this case
it's more appropriate to do enforce in this cases, with the reusable
exception.
@nemanja-boric-sociomantic
Copy link
Contributor Author

Updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants