August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #10 from Steven Schveighoffer <schveiguy@yahoo.com> 2010-08-12 06:43:37 PDT ---
(In reply to comment #8)
> C# and Java have safe finalizers.

According to the few pages I googled for C# finalizers, this is not true.  You are not supposed to access/attempt to destroy GC managed resources.  In fact, C# doesn't even allow manual deletion of such resources.  The only purpose of finalizers is to clean up non-GC resources.

After reading those pages, it looks like C# is almost identical in the restrictions and guarantees as D is.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #11 from Michel Fortin <michel.fortin@michelf.com> 2010-08-12 09:54:51 EDT ---
(In reply to comment #8)
> C# and Java have safe finalizers.

Indeed. Java allows resurrection, which means that if you leak a reference to an object during the collection, the GC won't collect that object and will leave it in the state it was after the finalizer was called.

This is an interesting idea, but I see two reasons it'll not work fro D. First, D doesn't emit special code notifying the GC when assigning to a pointer, so the GC would have to do a full scan again after a collection just to check if someone resurrected a memory block.

The second reason is the D2 multithreading model. The GC might run on a different thread. If you resurrect a non-shared object, that object won't live on the same thread anymore but might continue to reference non-shared memory from other threads. This is in violation of the thread-safe type system.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #12 from nfxjfg@gmail.com 2010-08-12 07:06:10 PDT ---
By the way, separation between finalizers and destructors has been in Tango for ages.

There's the Object.dipose() method. This method is only called on deterministic destruction, e.g on delete or with scope classes.

The "destructor" ~this is the finalizer and is always called, both on delete or on collection.

(This was done so mainly for backward comnpatibility, while still satisfying the need for knowing about deterministic destruction.)

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #13 from nfxjfg@gmail.com 2010-08-12 07:10:27 PDT ---
(In reply to comment #11)
> This is an interesting idea, but I see two reasons it'll not work fro D. First, D doesn't emit special code notifying the GC when assigning to a pointer, so the GC would have to do a full scan again after a collection just to check if someone resurrected a memory block.

This isn't so much of a problem if you assume objects with finalizers are rare. They can be collected in the next GC cycle.

> The second reason is the D2 multithreading model. The GC might run on a different thread. If you resurrect a non-shared object, that object won't live on the same thread anymore but might continue to reference non-shared memory from other threads. This is in violation of the thread-safe type system.

Good point. By definition, if the object is not shared(), the finalizer (or anything) must not run on a different thread. It doesn't matter if you access references or not. I wonder how D2 can have finalizers at all with this.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #14 from Michel Fortin <michel.fortin@michelf.com> 2010-08-12 10:46:23 EDT ---
(In reply to comment #13)
> This isn't so much of a problem if you assume objects with finalizers are rare. They can be collected in the next GC cycle.

I'm not sure we can make this assumption.

Beside, is it worth it? I mean, what is the use of a resurrected object beyond providing a little more safety? Given the multithreading model, we know finalizers can't be made safe; them being half-safer doesn't bring us much.

> Good point. By definition, if the object is not shared(), the finalizer (or anything) must not run on a different thread. It doesn't matter if you access references or not. I wonder how D2 can have finalizers at all with this.

Well, the object's memory block itself is no longer referenced by other threads (otherwise it would not be collected), so I guess as long as you only access values inside this memory block you're safe. You can probably also access non-GC memory you're the sole owner of, or non-GC shared memory. But you shouldn't access non-shared globals, or non-shared memory that someone else could have a reference to. This is starting to be really complicated, but except for the non-GC memory part it looks quite similar to the restrictions applied to methods of a synchronized class.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #15 from nfxjfg@gmail.com 2010-08-12 08:10:19 PDT ---
(In reply to comment #14)
> Beside, is it worth it? I mean, what is the use of a resurrected object beyond providing a little more safety? Given the multithreading model, we know finalizers can't be made safe; them being half-safer doesn't bring us much.

If you'd move finalizer execution to a dedicated finalizer thread, it'd be already quite safe. Then you can acquire locks to do synchronized access to your data.

But it still doesn't fit in D2's typesystem.

> Well, the object's memory block itself is no longer referenced by other threads (otherwise it would not be collected), so I guess as long as you only access values inside this memory block you're safe. You can probably also access non-GC memory you're the sole owner of, or non-GC shared memory. But you shouldn't access non-shared globals, or non-shared memory that someone else could have a reference to. This is starting to be really complicated, but except for the non-GC memory part it looks quite similar to the restrictions applied to methods of a synchronized class.

Running on a different thread still makes a severe difference to shared or C data. C APIs usually aren't thread-safe. For some OS APIs, the caller thread makes a difference (for instance, you'd break OS provided TLS).

You'll have to make an explicit exception in the language spec for finalizers to allow this.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 12, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #16 from Michel Fortin <michel.fortin@michelf.com> 2010-08-12 12:30:40 EDT ---
(In reply to comment #15)
> Running on a different thread still makes a severe difference to shared or C data. C APIs usually aren't thread-safe. For some OS APIs, the caller thread makes a difference (for instance, you'd break OS provided TLS).

This depends on the meaning of shared vs. non-shared. Is a non-shared object guarantied to always exist in the same thread? Or is it only guarantied to be accessible in one thread at a time? The former would forbid objects with a unique reference to a non-shared memory block from being moved from one thread to another with no copying, so I think the later is more useful.

> You'll have to make an explicit exception in the language spec for finalizers to allow this.

With the "accessible in one thread at a time" model, the only additional special case with multithreading is that you can't access non-shared memory referenced by a member if there's a chance this memory might still be in referenced by the original thread. Combine this with the problem of GC-managed memory which could have already be deallocated and multithreading only complicates the case where you have manually managed memory shared between objects (such as reference counting)...

... and I think I've found such a bug in std.containers.Array (added to bug
4624).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 31, 2010
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #17 from Michel Fortin <michel.fortin@michelf.com> 2010-08-31 07:27:54 EDT ---
In the event keeping a combined destructor-finalizer is the favored option, this could be done by repurposing the to-be-deprecated "scope" qualifier. "scope" could be applied as an attribute to structs and classes and would prevent the the struct/class from being allocated on the GC-heap. The absence of "scope" would make the destructor a finalizer and dereferencing a member would be prohibited in it without some kind of cast.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 05, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=4621


Maxim Fomin <maxim@maxim-fomin.ru> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |maxim@maxim-fomin.ru
         Resolution|                            |INVALID


--- Comment #18 from Maxim Fomin <maxim@maxim-fomin.ru> 2013-08-05 13:21:52 PDT ---
This is invalid report since @safe has nothing to do with accessing pointers/references which turned out to be nulls. This is valid D code:

void foo(int* p) @safe // or ref
{
   *p = 0;
}

void main() @safe
{
   foo(null);
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
August 05, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=4621



--- Comment #19 from Michel Fortin <michel.fortin@michelf.com> 2013-08-05 17:13:32 EDT ---
(In reply to comment #18)
> This is invalid report since @safe has nothing to do with accessing pointers/references which turned out to be nulls.

Maxim, you're the first to mention null here. I'm not sure I get your point.

This issue is about accessing destructed/deallocated memory from the destructor while GC is finalizing an object (or a struct on the GC heap). This can happen if you have a circular reference for instance, or anytime multiple objects that references themselves are finalized in the same pass.

The most evil thing you could do is to leak a reference to the a finalized object to the outer world, and then you have a pointer to deallocated memory lying around somewhere. There's no way to catch any of this (currently), hence why destructors are unsafe (when called from the GC).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
1 2
Next ›   Last »