www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unittest hangs on completion

reply David Zhang <straivers98 gmail.com> writes:
Hi,

I've noticed recently, that whenever I unittest, it program hangs 
either at the very end, or right before they start. When using 
vanilla unit tests, the program appears to hang after the "All 
unit tests have been completed successfully." message, and I have 
to force to program to exit. However, when I use unit-threaded 
for my unit testing, it gets to the "Running unit tests in dirs 
["."]" and then refuses to do anything more. Strangely enough, 
sometimes commenting out one or two tests seems to fix the issue 
with unit-threaded, only to resurface when I write a new test. 
Just what is going on?
Dec 29 2016
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 29 December 2016 at 20:27:21 UTC, David  Zhang wrote:
 Hi,

 I've noticed recently, that whenever I unittest, it program 
 hangs either at the very end, or right before they start. When 
 using vanilla unit tests, the program appears to hang after the 
 "All unit tests have been completed successfully." message, and 
 I have to force to program to exit. However, when I use 
 unit-threaded for my unit testing, it gets to the "Running unit 
 tests in dirs ["."]" and then refuses to do anything more. 
 Strangely enough, sometimes commenting out one or two tests 
 seems to fix the issue with unit-threaded, only to resurface 
 when I write a new test. Just what is going on?
It would be very helpful if you could provide example code that triggers that behavior.
Dec 29 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Thursday, 29 December 2016 at 20:33:33 UTC, Stefan Koch wrote:
 It would be very helpful if you could provide example code that 
 triggers that behavior.
I'd love to, but I'm not actually sure just what it is that breaks it. I can provide the git repo for one of them though though: https://gitlab.com/Straivers/Weave https://gitlab.com/Straivers/Weave.git
Dec 29 2016
parent David Zhang <straivers98 gmail.com> writes:
On Thursday, 29 December 2016 at 20:50:54 UTC, David  Zhang wrote:
 On Thursday, 29 December 2016 at 20:33:33 UTC, Stefan Koch 
 wrote:
 It would be very helpful if you could provide example code 
 that triggers that behavior.
I'd love to, but I'm not actually sure just what it is that breaks it. I can provide the git repo for one of them though though: https://gitlab.com/Straivers/Weave https://gitlab.com/Straivers/Weave.git
Ok, so after further fiddling, it seems to originate from here: [from the list class] safe unittest { class A { int foo; } auto list = new List!A(); list ~= [new A, new A, new A]; assert(list.findElement(null).result == null); } However, commenting it out, and replacing it with another block (the test immediately below it, causes it to hang too, or error out and crash without any error message.
Dec 29 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/29/16 3:27 PM, David Zhang wrote:
 Hi,

 I've noticed recently, that whenever I unittest, it program hangs either
 at the very end, or right before they start. When using vanilla unit
 tests, the program appears to hang after the "All unit tests have been
 completed successfully." message, and I have to force to program to
 exit. However, when I use unit-threaded for my unit testing, it gets to
 the "Running unit tests in dirs ["."]" and then refuses to do anything
 more. Strangely enough, sometimes commenting out one or two tests seems
 to fix the issue with unit-threaded, only to resurface when I write a
 new test. Just what is going on?
Where does the "All unit tests have been completed successfully." message come from? That's not standard D, which prints nothing. -Steve
Dec 29 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Friday, 30 December 2016 at 00:44:50 UTC, Steven Schveighoffer 
wrote:
 Where does the "All unit tests have been completed 
 successfully." message come from? That's not standard D, which 
 prints nothing.

 -Steve
I should have mentioned that I use dub then, shouldn't I? Anyway, this is what I get: C:\Users\David\Projects\weave>dub test Generating test runner configuration '__test__library__' for 'library' (library). Performing "unittest" build using dmd for x86. weave ~master: building configuration "__test__library__"... Linking... Running .\bin\__test__library__.exe All unit tests have been run successfully.
Dec 29 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/29/16 7:49 PM, David Zhang wrote:
 On Friday, 30 December 2016 at 00:44:50 UTC, Steven Schveighoffer wrote:
 Where does the "All unit tests have been completed successfully."
 message come from? That's not standard D, which prints nothing.
I should have mentioned that I use dub then, shouldn't I? Anyway, this is what I get: C:\Users\David\Projects\weave>dub test Generating test runner configuration '__test__library__' for 'library' (library). Performing "unittest" build using dmd for x86. weave ~master: building configuration "__test__library__"... Linking... Running .\bin\__test__library__.exe All unit tests have been run successfully.
Looks like that comes from here: https://github.com/dlang/dub/blob/master/source/dub/dub.d#L577 I have serious doubts that this is the correct way to run tests, as share ctors are supposed to have run BEFORE unit tests are allowed to. It's quite possible (and likely) that unit tests are running before critical other shared ctors have completed, which is why something isn't working. In any case, it looks like the unit tests have run, and run successfully, and then main has run, and then it's the teardown of the runtime that's hanging. I'm not 100% certain, because I don't know exactly how this code comes into play -- it's a string in this file. -Steve
Dec 29 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Friday, 30 December 2016 at 01:25:50 UTC, Steven Schveighoffer 
wrote:
 Looks like that comes from here:

 https://github.com/dlang/dub/blob/master/source/dub/dub.d#L577

 I have serious doubts that this is the correct way to run 
 tests, as share ctors are supposed to have run BEFORE unit 
 tests are allowed to. It's quite possible (and likely) that 
 unit tests are running before critical other shared ctors have 
 completed, which is why something isn't working.

 In any case, it looks like the unit tests have run, and run 
 successfully, and then main has run, and then it's the teardown 
 of the runtime that's hanging.

 I'm not 100% certain, because I don't know exactly how this 
 code comes into play -- it's a string in this file.

 -Steve
Huh, shouldn't this problem have manifested itself earlier then? Why only now? I don't remember this happening previously. At the very least I'm pretty sure I first encountered it last week. Unless something in DMD was patched? But then there'd be no way I'm the only one encountering this problem, and there don't appear to be any issues on github's tracker.
Dec 29 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/29/16 8:33 PM, David Zhang wrote:
 On Friday, 30 December 2016 at 01:25:50 UTC, Steven Schveighoffer wrote:
 Looks like that comes from here:

 https://github.com/dlang/dub/blob/master/source/dub/dub.d#L577

 I have serious doubts that this is the correct way to run tests, as
 share ctors are supposed to have run BEFORE unit tests are allowed to.
 It's quite possible (and likely) that unit tests are running before
 critical other shared ctors have completed, which is why something
 isn't working.

 In any case, it looks like the unit tests have run, and run
 successfully, and then main has run, and then it's the teardown of the
 runtime that's hanging.

 I'm not 100% certain, because I don't know exactly how this code comes
 into play -- it's a string in this file.
Huh, shouldn't this problem have manifested itself earlier then? Why only now? I don't remember this happening previously.
This is one of those very fragile and seemingly random types of things. A change in code, a change in linker order, maybe even random chance, can cause it to fail or not.
 At the very least
 I'm pretty sure I first encountered it last week. Unless something in
 DMD was patched? But then there'd be no way I'm the only one
 encountering this problem, and there don't appear to be any issues on
 github's tracker.
It depends on what is actually hanging the process. If it's something in your code base only, then nobody else would be seeing it. -Steve
Dec 30 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Friday, 30 December 2016 at 14:12:35 UTC, Steven Schveighoffer 
wrote:
 [snip]
 It depends on what is actually hanging the process. If it's 
 something in your code base only, then nobody else would be 
 seeing it.

 -Steve
So, what should I do with it? I'd submit a bug report, but I don't know how to isolate the bug, and thus where to submit it. Really, I'd just be happy knowing if it's my code or not.
Dec 30 2016
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/30/16 1:03 PM, David Zhang wrote:
 On Friday, 30 December 2016 at 14:12:35 UTC, Steven Schveighoffer wrote:
 [snip]
 It depends on what is actually hanging the process. If it's something
 in your code base only, then nobody else would be seeing it.
So, what should I do with it? I'd submit a bug report, but I don't know how to isolate the bug, and thus where to submit it. Really, I'd just be happy knowing if it's my code or not.
Can you debug and see where it's hanging? Note that you can use dub -v I believe to see all the lines being executed by the dub process. -Steve
Dec 30 2016
prev sibling parent reply Seb <seb wilzba.ch> writes:
On Friday, 30 December 2016 at 18:03:44 UTC, David  Zhang wrote:
 On Friday, 30 December 2016 at 14:12:35 UTC, Steven 
 Schveighoffer wrote:
 [snip]
 It depends on what is actually hanging the process. If it's 
 something in your code base only, then nobody else would be 
 seeing it.

 -Steve
So, what should I do with it? I'd submit a bug report, but I don't know how to isolate the bug, and thus where to submit it. Really, I'd just be happy knowing if it's my code or not.
Dustmite (https://github.com/CyberShadow/DustMite) is your friend! Specifically with a timeout command (e.g. https://github.com/CyberShadow/DustMite/wiki/Running-commands-with-a-timeout) and checking whether your reduced code is still timing out (exit code of timeout is 124). If you set it to sth. reasonable (e.g. 10s) and use multiple threads (e.g. -j32) it shouldn't take that long to have a reduced code example.
Dec 30 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Friday, 30 December 2016 at 20:59:30 UTC, Seb wrote:
 On Friday, 30 December 2016 at 18:03:44 UTC, David  Zhang wrote:
 On Friday, 30 December 2016 at 14:12:35 UTC, Steven 
 Schveighoffer wrote:
 [snip]
 It depends on what is actually hanging the process. If it's 
 something in your code base only, then nobody else would be 
 seeing it.

 -Steve
So, what should I do with it? I'd submit a bug report, but I don't know how to isolate the bug, and thus where to submit it. Really, I'd just be happy knowing if it's my code or not.
Dustmite (https://github.com/CyberShadow/DustMite) is your friend! Specifically with a timeout command (e.g. https://github.com/CyberShadow/DustMite/wiki/Running-commands-with-a-timeout) and checking whether your reduced code is still timing out (exit code of timeout is 124). If you set it to sth. reasonable (e.g. 10s) and use multiple threads (e.g. -j32) it shouldn't take that long to have a reduced code example.
There wouldn't happen to be an alternative on windows without installing cygwin would there? I don't have access to a linux machine at the moment. Steven, the process hangs after the "All unit tests have been successfully", after which the process is supposed to exit immediately. -- Running .\bin\__test__library__.exe All unit tests have been run successfully. ^C --
Dec 30 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/30/16 4:31 PM, David Zhang wrote:
  Steven, the process hangs after the "All unit tests have been
 successfully", after which the process is supposed to exit immediately.
What is actually happening is that the D main function returns. Then the D runtime tears down everything, including joining all threads, running all module static dtors, terminating the GC, etc. Then it returns to the OS the exit code. So it's likely somewhere in there that it's hanging. -Steve
Dec 30 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Friday, 30 December 2016 at 22:42:07 UTC, Steven Schveighoffer 
wrote:
 What is actually happening is that the D main function returns. 
 Then the D runtime tears down everything, including joining all 
 threads, running all module static dtors, terminating the GC, 
 etc.

 Then it returns to the OS the exit code.

 So it's likely somewhere in there that it's hanging.

 -Steve
I've tried manually reducing the code to find the problem. This is what I've got. I'm not sure how to reduce it further. class Foo { ~this() { S* next, current; next = current = _foo; while (next) { next = current.next; theAllocator.dispose(current); } } void insert(Range)(Range range) { foreach (e; range) _foo = theAllocator.make!S(_foo); } S* _foo; struct S { S* next; } } unittest { auto list = new Foo(); list.insert([1, 2, 3, 4]); }
Dec 30 2016
parent reply David Zhang <straivers98 gmail.com> writes:
Extracting everything into a main() also causes the application 
to hang.

ie:

struct S
{
     S* next;
}

S* _foo;
foreach (e; 0 .. 10)
     _foo = theAllocator.make!S(_foo);

S* next, current;
next = current = _foo;
while (next)
{
     next = current.next;
     theAllocator.dispose(current);
}
Dec 30 2016
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 31/12/2016 2:52 PM, David Zhang wrote:
 Extracting everything into a main() also causes the application to hang.

 ie:

 struct S
 {
     S* next;
 }

 S* _foo;
 foreach (e; 0 .. 10)
     _foo = theAllocator.make!S(_foo);

 S* next, current;
 next = current = _foo;
 while (next)
 {
     next = current.next;
     theAllocator.dispose(current);
 }
As it should, current is never reassigned. You only need one var, next. Of course I didn't read the entire thread chain so, I'm probably missing something. import std.experimental.allocator; void main() { struct S { S* next; } S* _foo; foreach (e; 0 .. 10) _foo = theAllocator.make!S(_foo); S* next; next = _foo; while(next !is null) { auto nextT = next.next; theAllocator.dispose(next); next = nextT; } }
Dec 30 2016
parent reply David Zhang <straivers98 gmail.com> writes:
On Saturday, 31 December 2016 at 02:03:07 UTC, rikki cattermole 
wrote:
 As it should, current is never reassigned.
 You only need one var, next. Of course I didn't read the entire 
 thread chain so, I'm probably missing something.

 import std.experimental.allocator;

 void main() {
         struct S { S* next; }
         S* _foo;
         foreach (e; 0 .. 10)
                 _foo = theAllocator.make!S(_foo);
         S* next;

         next = _foo;
         while(next !is null) {
                 auto nextT = next.next;
                 theAllocator.dispose(next);
                 next = nextT;
         }
 }
Thanks for your response. So next is never null, and thus there is an infinite loop, correct? If so, why does dub indicate that all tests are complete?
Dec 30 2016
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 31/12/2016 3:32 PM, David Zhang wrote:
 On Saturday, 31 December 2016 at 02:03:07 UTC, rikki cattermole wrote:
 As it should, current is never reassigned.
 You only need one var, next. Of course I didn't read the entire thread
 chain so, I'm probably missing something.

 import std.experimental.allocator;

 void main() {
         struct S { S* next; }
         S* _foo;
         foreach (e; 0 .. 10)
                 _foo = theAllocator.make!S(_foo);
         S* next;

         next = _foo;
         while(next !is null) {
                 auto nextT = next.next;
                 theAllocator.dispose(next);
                 next = nextT;
         }
 }
Thanks for your response. So next is never null, and thus there is an infinite loop, correct? If so, why does dub indicate that all tests are complete?
No, my understand is thus: next = current.next; theAllocator.dispose(current); When current is deallocated, current is pointing to free'd memory. After that point it should be segfaulting when you try to access it *I think*.
Dec 30 2016
parent David Zhang <straivers98 gmail.com> writes:
On Saturday, 31 December 2016 at 02:36:01 UTC, rikki cattermole 
wrote:
 No, my understand is thus:

  next = current.next;
  theAllocator.dispose(current);

 When current is deallocated, current is pointing to free'd 
 memory.
 After that point it should be segfaulting when you try to 
 access it *I think*.
Huh, alright. I see what you mean. I'll keep this in mind.
Dec 30 2016
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/30/16 9:32 PM, David Zhang wrote:
 On Saturday, 31 December 2016 at 02:03:07 UTC, rikki cattermole wrote:
 As it should, current is never reassigned.
 You only need one var, next. Of course I didn't read the entire thread
 chain so, I'm probably missing something.

 import std.experimental.allocator;

 void main() {
         struct S { S* next; }
         S* _foo;
         foreach (e; 0 .. 10)
                 _foo = theAllocator.make!S(_foo);
         S* next;

         next = _foo;
         while(next !is null) {
                 auto nextT = next.next;
                 theAllocator.dispose(next);
                 next = nextT;
         }
 }
Thanks for your response. So next is never null, and thus there is an infinite loop, correct?
This is correct. Rewriting the code as rikki has would be correct code. However, I would also caution about your original code that deallocates inside a destructor. You cannot access nor try to free reference members of a GC-allocated class inside the destructor. It's explicitly cautioned against here: http://dlang.org/spec/class.html#destructors "Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects."
 If so, why does dub indicate that all tests are
 complete?
Because dub is running the tests and then running your main. Unit tests run before main does. -Steve
Jan 01