July 04, 2021

On Saturday, 3 July 2021 at 22:52:39 UTC, frame wrote:

>

On Saturday, 3 July 2021 at 22:04:04 UTC, Luis wrote:

>

scope(exit) it's syntactic sugar for a classic try {} finally {} . The documentation says that must be executed.

It works if you replace printf() with writeln() or use writeln() after. There must be some buffer issue.

Not works as you expected.
Yes, replacing by writeln (better said, putting a writeln) makes it to work. More weird, if I replace the printf(...) by a fprintf(stderr, ...), I don't get anything.

To discard depening on checking if it works by the side effect of writing something on the console, now i using malloc/free and checking with valgrind for a lost memory :

#!/usr/bin/env dub
/+ dub.sdl:
  dependency "pijamas" version="~>1.1"
+/
import core.exception;
import core.stdc.stdio;
import core.stdc.stdlib;
import std.stdio : writeln;


void main()  {
  import pijamas;

  should(() {
    int* ptr = cast(int*) malloc(int.sizeof * 1000);
    try {
      fprintf(stderr, "Hello\n");
      throw new RangeError("bla bla");
    } finally {
      // writeln("Bye 1");
      fprintf(stderr, "Bye\n");
      free(ptr);
    }
  }).Throw!RangeError;
}

Outputs this :

$ f.d
Hello

And valgrind outputs (using the temporal executable generated by dub on /tmp/.dub/...) :

$ valgrind --leak-check=full /tmp/.dub/build/f-\~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f
==18356== Memcheck, a memory error detector
==18356== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18356== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==18356== Command: /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f
==18356==
--18356-- WARNING: Serious error when reading debug info
--18356-- When reading debug info from /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f:
--18356-- DWARF line info appears to be corrupt - the section is too small
--18356-- WARNING: Serious error when reading debug info
--18356-- When reading debug info from /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f:
--18356-- read_filename_table: .debug_line is missing?
Hello
==18356==
==18356== HEAP SUMMARY:
==18356==     in use at exit: 4,056 bytes in 3 blocks
==18356==   total heap usage: 231 allocs, 228 frees, 76,436 bytes allocated
==18356==
==18356== 32 bytes in 1 blocks are possibly lost in loss record 2 of 3
==18356==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind
....
==18356== 4,000 bytes in 1 blocks are definitely lost in loss record 3 of 3
==18356==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18356==    by 0x16873D: _D1f4mainFZ9__lambda1FNbZv (f.d:15)
==18356==    by 0x168B2F: _D7pijamas9assertion__T9AssertionTPFNbZvZQs__T5ThrowHTC4core9exception10RangeErrorZQBlMFNeAyamZv (assertion.d:602)
==18356==    by 0x168724: _Dmain (assertion.d:598)
==18356==    by 0x18C962: _D2rt6dmain212_d_run_main2UAAamPUQgZiZ6runAllMFZ9__lambda2MFZv (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x18C804: _D2rt6dmain212_d_run_main2UAAamPUQgZiZ7tryExecMFMDFZvZv (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x18C8DE: _D2rt6dmain212_d_run_main2UAAamPUQgZiZ6runAllMFZv (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x18C804: _D2rt6dmain212_d_run_main2UAAamPUQgZiZ7tryExecMFMDFZvZv (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x18C765: _d_run_main2 (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x18C4C1: _d_run_main (in /tmp/.dub/build/f-~master/application-debug-linux.posix-x86_64-dmd_2097-FB7AFBA927D99FA3DDD7307BACA865DA/f)
==18356==    by 0x1687B9: main (entrypoint.d:29)
==18356==
==18356== LEAK SUMMARY:
==18356==    definitely lost: 4,000 bytes in 1 blocks
==18356==    indirectly lost: 0 bytes in 0 blocks
==18356==      possibly lost: 32 bytes in 1 blocks
==18356==    still reachable: 24 bytes in 1 blocks
==18356==         suppressed: 0 bytes in 0 blocks
==18356== Reachable blocks (those to which a pointer was found) are not shown.
==18356== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==18356==
==18356== For lists of detected and suppressed errors, rerun with: -s
==18356== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

But the most funny thing, it's that if I remove the comment on the writeln, then just works and the memory leak dissapers! DMD , it's doing very weird things here, only executing the scope (exit) {} / finally {} when there is a side effect with writeln !
This not happens with my local install of ldc2.

On my original post, I was just searching for a memory leak on a tests of my private library, where I have some @nogc containers using std.experimental.allocator, and expecting that the scope (exit) being executed and releasing the allocated memory. And this wasn't happening.

dmd --version :
DMD64 D Compiler v2.097.0

ldc2 -v :
binary /usr/bin/ldc2
version 1.20.1 (DMD v2.090.1, LLVM 10.0.0)
config /etc/ldc2.conf (x86_64-pc-linux-gnu)

July 04, 2021

On Sunday, 4 July 2021 at 08:24:36 UTC, Luis wrote:

>

On Saturday, 3 July 2021 at 22:52:39 UTC, frame wrote:

>

It works if you replace printf() with writeln() or use writeln() after. There must be some buffer issue.

Not works as you expected.
Yes, replacing by writeln (better said, putting a writeln) makes it to work. More weird,

Dennis's explanation makes the most sense:

writeln can throw an Exception, so its presence prevents nothrow inference, which otherwise permits the (not intended to be catchable) RangeError to exit without properly unwinding the stack.

By that, what you're running into is an unpleasant interaction between

  1. scope(exit)s that you're writing
  2. Errors being thrown rather than Exceptions
  3. anonymous functions getting inferred as nothrow

And a resolution could be to submit an issue to make #1 prevent #3: if someone wants nothrow on an anonoymous function with scope guards, they have to make it explicit.

And while checking for prior issues like this, I found

https://issues.dlang.org/show_bug.cgi?id=17494

>

Not cleaning up after an Error is thrown is allowed by the D spec. This enhancement allows much better code to be generated for nothrow code when scope is used. It will also not unwind declarations with destructors in nothrow code when Errors are thrown.

I read this initially as "it is a bug for scope(exit) to ever run after an Error is thrown", but it's an optimization for nothrow code, which fits what you're seeing.

July 04, 2021

On Sunday, 4 July 2021 at 10:07:08 UTC, jfondren wrote:

>

By that, what you're running into is an unpleasant interaction between

  1. scope(exit)s that you're writing
  2. Errors being thrown rather than Exceptions
  3. anonymous functions getting inferred as nothrow
>

And a resolution could be to submit an issue to make #1 prevent #3: if someone wants nothrow on an anonoymous function with scope guards, they have to make it explicit.

Although taking the example code and giving the anonymous function a name does not actually change this behavior. It really has to potentially throw a non-Error exception, like

if (ptr is null) throw new Exception("null");

There are options like "add a debugging flag to not treat Errors differently"... but you know, you're going to have gaps in what you can reasonably test anyway, like code that calls libc exit(), or code that SIGKILLs itself, or code that enters an infinite loop. You could document some issues with catching non-catchable exceptions.

July 04, 2021

On Sunday, 4 July 2021 at 10:07:08 UTC, jfondren wrote:

>

On Sunday, 4 July 2021 at 08:24:36 UTC, Luis wrote:

Dennis's explanation makes the most sense:

writeln can throw an Exception, so its presence prevents nothrow inference, which otherwise permits the (not intended to be catchable) RangeError to exit without properly unwinding the stack.

By that, what you're running into is an unpleasant interaction between

  1. scope(exit)s that you're writing
  2. Errors being thrown rather than Exceptions
  3. anonymous functions getting inferred as nothrow

I did https://issues.dlang.org/show_bug.cgi?id=22099

July 04, 2021

On Sunday, 4 July 2021 at 10:07:08 UTC, jfondren wrote:

> >

Not cleaning up after an Error is thrown is allowed by the D spec. This enhancement allows much better code to be generated for nothrow code when scope is used. It will also not unwind declarations with destructors in nothrow code when Errors are thrown.

I read this initially as "it is a bug for scope(exit) to ever run after an Error is thrown", but it's an optimization for nothrow code, which fits what you're seeing.

So it's might not a bug but not well documented.

scope(...) still acts as try... catch(Throwable)... finally as logic people would expect although not written in stone anywhere.

Code within that blocks that are not seen by the compiler as 'nothrow' will be executed. But the compiler may silently mark code as 'nothrow' if possible and then this "feature" gets enabled and cleanup will be discarded.

1 2
Next ›   Last »