March 17, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Graham St Jack | Graham St Jack wrote: > Another question along these lines - I am having trouble with a multi- > threaded program (linux, gdc, phobos). It works fine until main() returns, and then I get a SIGUSR1 that causes the program to crash. Any ideas what might be happening? > > My investigations so far show that SIGUSR1 and SIGUSR2 are used in thread.d as part of the implementation of pause() and resume(). I was under the impression that the thread that is interrupted to handle a signal is chosen at random, and therefore that it is a bad idea to try to use signals in a threaded program, so I am confused. > > Also, the garbage collector seems to kill off all my threads without waiting for their run methods to return. I assume this means that the threads aren't being treated as root objects by the garbage collector. When main() exits, the GC starts a collection. In order to do so safely it pauses all other threads with Thread.pauseAll, which (in the Linux version) sends SIGUSR1 to all other threads to tell them to pause. (Note that this doesn't kill them, even though the function that does this is called pthread_kill) It's usually a good idea to just tell your debugger to ignore SIGUSR1 and SIGUSR2 (the latter is used to resume afterwards). To do this in GDB, use "handle SIGUSR1 noprint nostop SIGUSR2 noprint nostop". If you're just wondering why all your threads get killed when main() ends I can tell you right now: this is how it works :P. You should see the same in a C or C++ program. > Does all this mean that I have to explicitly take action so that the run methods will return, then wait() on them before returning from main()? As you said, call Thread.wait() on all other threads before exiting main() to wait for them to end. Or just switch to Tango which does this automatically (add Tangobos if you need Phobos compatibility). | |||
March 17, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Vladimir Panteleev | On Mon, 17 Mar 2008 06:06:49 +0300, Vladimir Panteleev <thecybershadow@gmail.com> wrote:
> On Mon, 17 Mar 2008 00:28:04 +0200, Graham St Jack <grahams@acres.com.au> wrote:
>
>> I agree that this is a problem. Who cares if the two objects in question
>> aren't referenced and more - their destructors should be called in the
>> right order if it is possible to do so.
>
> This is impossible without an exact garbage collector.
>
> Also: circular references.
>
Indeed, it's hard to implement if possible. And it is a design problem if not possible.
But if it is so bad, then we need a safe pattern that would avoid this problem. And maybe build it into language somehow???
Disabling access to other objects in a destructor is really a bad option...
I would also be happy to hear Walter's comment on this.
| |||
March 17, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | On Mon, 17 Mar 2008 10:16:57 +0100, Frits van Bommel wrote:
> Graham St Jack wrote:
>> Another question along these lines - I am having trouble with a multi- threaded program (linux, gdc, phobos). It works fine until main() returns, and then I get a SIGUSR1 that causes the program to crash. Any ideas what might be happening?
>>
>> My investigations so far show that SIGUSR1 and SIGUSR2 are used in thread.d as part of the implementation of pause() and resume(). I was under the impression that the thread that is interrupted to handle a signal is chosen at random, and therefore that it is a bad idea to try to use signals in a threaded program, so I am confused.
>>
>> Also, the garbage collector seems to kill off all my threads without waiting for their run methods to return. I assume this means that the threads aren't being treated as root objects by the garbage collector.
>
> When main() exits, the GC starts a collection. In order to do so safely
> it pauses all other threads with Thread.pauseAll, which (in the Linux
> version) sends SIGUSR1 to all other threads to tell them to pause. (Note
> that this doesn't kill them, even though the function that does this is
> called pthread_kill)
> It's usually a good idea to just tell your debugger to ignore SIGUSR1
> and SIGUSR2 (the latter is used to resume afterwards). To do this in
> GDB, use "handle SIGUSR1 noprint nostop SIGUSR2 noprint nostop".
>
> If you're just wondering why all your threads get killed when main() ends I can tell you right now: this is how it works :P. You should see the same in a C or C++ program.
>
>> Does all this mean that I have to explicitly take action so that the
>> run methods will return, then wait() on them before returning from
>> main()?
>
> As you said, call Thread.wait() on all other threads before exiting
> main() to wait for them to end. Or just switch to Tango which does this
> automatically (add Tangobos if you need Phobos compatibility).
Thanks for the information - it has saved me a lot of wasted time.
| |||
March 17, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Vladimir Panteleev | Vladimir Panteleev wrote:
> On Sun, 16 Mar 2008 17:58:40 +0200, Christopher Wright <dhasenan@gmail.com> wrote:
>
>> Getting around this would not be extremely difficult, I think.
>
> You could also write custom class allocators and use non-managed memory for manual memory management:
>
> http://www.digitalmars.com/d/1.0/class.html#allocators
>
Then your destructors must delete all their owned children and free that memory. Fortunately, opDelete is inherited, so they just have to delete their children.
| |||
March 17, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Koroskin Denis | "Koroskin Denis" in message
> On Mon, 17 Mar 2008 01:28:04 +0300, Graham St Jack wrote:
>> I agree that this is a problem. Who cares if the two objects in question aren't referenced and more - their destructors should be called in the right order if it is possible to do so.
>
> Yep.
>
>> I guess the work-around (and maybe permanent) is to not reference any garbage-collected objects in your destructors.
>>
>
> Nope. This is against RAII pattern.
The issue is with the usage of the destructor to clean up memory. Do not worry about cleaning up memory in destructors, that is what we have the garbage collector for.
In your example, all the members of Resource are GC allocated and so will be cleaned up when Resource is cleaned up. You shouldn't care what the order is.
If you have an OS resource, such as an open file, that should be cleaned up in the destructor of the object which owns the OS handle. This is what destructors are for.
The only missing piece is if you wanted to have an array of OS resources. In order to make sure the elements in this array are cleaned up properly, you would need to allocate the array using an alternate memory allocator that does not get cleaned up automatically, or else use wrapper classes to store the OS resources. A way to fix this would be to always deallocate arrays last in the GC, and allow using the array elements (not objects pointed to by those elements) in the destructors.
-Steve
| |||
March 18, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Mon, 17 Mar 2008 12:34:06 -0400, Steven Schveighoffer wrote:
> "Koroskin Denis" in message
>> On Mon, 17 Mar 2008 01:28:04 +0300, Graham St Jack wrote:
>>> I agree that this is a problem. Who cares if the two objects in question aren't referenced and more - their destructors should be called in the right order if it is possible to do so.
>>
>> Yep.
>>
>>> I guess the work-around (and maybe permanent) is to not reference any garbage-collected objects in your destructors.
>>>
>>>
>> Nope. This is against RAII pattern.
>
> The issue is with the usage of the destructor to clean up memory. Do not worry about cleaning up memory in destructors, that is what we have the garbage collector for.
>
> In your example, all the members of Resource are GC allocated and so will be cleaned up when Resource is cleaned up. You shouldn't care what the order is.
>
> If you have an OS resource, such as an open file, that should be cleaned up in the destructor of the object which owns the OS handle. This is what destructors are for.
>
> The only missing piece is if you wanted to have an array of OS resources. In order to make sure the elements in this array are cleaned up properly, you would need to allocate the array using an alternate memory allocator that does not get cleaned up automatically, or else use wrapper classes to store the OS resources. A way to fix this would be to always deallocate arrays last in the GC, and allow using the array elements (not objects pointed to by those elements) in the destructors.
>
> -Steve
I get it now. Thanks for the insights. I am too used to manual memory- management in C++ - using a garbage collector is wonderfully easy by comparison, but there are a few tricks to learn even so.
| |||
March 18, 2008 Re: GC BUG: Referenced object destroyed before released | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Graham St Jack | On Mon, 17 Mar 2008 12:27:15 +0000, Graham St Jack wrote:
> On Mon, 17 Mar 2008 10:16:57 +0100, Frits van Bommel wrote:
>
>> Graham St Jack wrote:
>>> Another question along these lines - I am having trouble with a multi- threaded program (linux, gdc, phobos). It works fine until main() returns, and then I get a SIGUSR1 that causes the program to crash. Any ideas what might be happening?
>>>
>>> My investigations so far show that SIGUSR1 and SIGUSR2 are used in thread.d as part of the implementation of pause() and resume(). I was under the impression that the thread that is interrupted to handle a signal is chosen at random, and therefore that it is a bad idea to try to use signals in a threaded program, so I am confused.
>>>
>>> Also, the garbage collector seems to kill off all my threads without waiting for their run methods to return. I assume this means that the threads aren't being treated as root objects by the garbage collector.
>>
>> When main() exits, the GC starts a collection. In order to do so safely
>> it pauses all other threads with Thread.pauseAll, which (in the Linux
>> version) sends SIGUSR1 to all other threads to tell them to pause.
>> (Note that this doesn't kill them, even though the function that does
>> this is called pthread_kill)
>> It's usually a good idea to just tell your debugger to ignore SIGUSR1
>> and SIGUSR2 (the latter is used to resume afterwards). To do this in
>> GDB, use "handle SIGUSR1 noprint nostop SIGUSR2 noprint nostop".
>>
>> If you're just wondering why all your threads get killed when main() ends I can tell you right now: this is how it works :P. You should see the same in a C or C++ program.
>>
>>> Does all this mean that I have to explicitly take action so that the
>>> run methods will return, then wait() on them before returning from
>>> main()?
>>
>> As you said, call Thread.wait() on all other threads before exiting
>> main() to wait for them to end. Or just switch to Tango which does this
>> automatically (add Tangobos if you need Phobos compatibility).
>
> Thanks for the information - it has saved me a lot of wasted time.
Just to let you know, I fixed my code along these lines and it works just fine. Thanks for the leg up.
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply