[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #17 from Michel Fortin michel.for...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 Steven Schveighoffer schvei...@yahoo.com changed: What|Removed |Added CC||schvei...@yahoo.com --- Comment #7 from Steven Schveighoffer schvei...@yahoo.com 2010-08-12 05:21:10 PDT --- (In reply to comment #6) It could be an attribute, or it could be something else. For instance, instead of having just destructors, we could have destructors (~this) and finalizers (~~this). A struct with neither can go anywhere, a struct with a destructor but no finalizer cannot go on the GC-heap, a struct with only a finalizer can go anywhere (the finalizer is used as the destructor), and a struct with both can go anywhere. The finalizer cannot be made @safe. I think rather than prevent where these items should go, you should just not call the destructor when the struct/class is being destroyed by the GC. I'd say you could even prevent what the finalizer contains, but it's too limiting for the compiler to assume what type of memory a reference is referencing. My thought is simply that a finalizer is not safe, and a destructor is safe. That at least gives a path for implementation (just mark your finalizer as @trusted, and it can be used in SafeD). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 nfx...@gmail.com changed: What|Removed |Added CC||nfx...@gmail.com --- Comment #8 from nfx...@gmail.com 2010-08-12 06:20:27 PDT --- *facepalm* C# and Java have safe finalizers. There's nothing that stops you rewriting the finalization part in gcx.d to make it safe. The only real issue is that finalizers are not deterministic (unsolvable) and that finalizers are invoked in an arbitrary context (solvable if you'd create a finalization worker thread or so). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #9 from Michel Fortin michel.for...@michelf.com 2010-08-12 09:37:20 EDT --- (In reply to comment #7) I think rather than prevent where these items should go, you should just not call the destructor when the struct/class is being destroyed by the GC. One thing is that I fear it becomes too easy to write a destructor and then forget the finalizer, which would results in leaks everywhere. So I think there has to be something to remind people that they need a finalizer when they need to dispose of some resource and the struct is allocated on the heap. But just not calling the finalizer is still a better option than calling the destructor during collection: a leak is less harmful than memory corruption. Forbidding GC-allocated structs could be useful to make some RAII patterns safer too. For instance, think of a struct representing a database transaction: it should probably live on the stack, you certainly don't want it to be garbaged collected. The compiler enforcing deterministic destruction would improve program correctness for this kind of thing. I'd say you could even prevent what the finalizer contains, but it's too limiting for the compiler to assume what type of memory a reference is referencing. My thought is simply that a finalizer is not safe, and a destructor is safe. That at least gives a path for implementation (just mark your finalizer as @trusted, and it can be used in SafeD). Whatever it is, I'm now of the opinion that the limitation should only apply to SafeD. The limitation could be about dereferencing members and calling functions that might dereference a member, or it could be that the finalizer itself is not allowed to be safe. Allowing non-dereferencing actions in the finalizer in SafeD would still allow a few things, such as printing a Hello I'm finalized! message on the console or updating a global variable (such as a counter of live objects). Nothing very interesting though. But it'd also allow an empty finalizer to be @safe, which might be useful if the presence of a destructor and the absence of a finalizer prevents allocation on the GC-heap. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #10 from Steven Schveighoffer schvei...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #11 from Michel Fortin michel.for...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #12 from nfx...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #13 from nfx...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #14 from Michel Fortin michel.for...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #15 from nfx...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #16 from Michel Fortin michel.for...@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: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #2 from Michel Fortin michel.for...@michelf.com 2010-08-11 14:37:52 EDT --- Perhaps a better solution for structs: have a way to distinguish between a struct that can be put on the GC heap and one that cannot. A struct that cannot go on the GC heap make it safe to access GC-managed members in its destructor, and thus can have a @safe destructor. But at the same time such a struct would be prohibited at compile time from being part of a class, or from being allocated with new (either solitary or part of an array). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 Jonathan M Davis jmdavisp...@gmail.com changed: What|Removed |Added CC||jmdavisp...@gmail.com --- Comment #1 from Jonathan M Davis jmdavisp...@gmail.com 2010-08-11 11:36:33 PDT --- What about structs which are on the stack? I agree that the stuff on the heap has this problem, but structs on the stack should be fine, shouldn't they? I'd hate for structs on the stack not be able to have destructors except in SafeD. It would make RAII only work in SafeD, which would not be good. I do agree that destructors on the heap should be disallowed in SafeD, but I don't want to see structs on the heap not being allowed to have destructors in SafeD. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #3 from Jonathan M Davis jmdavisp...@gmail.com 2010-08-11 11:43:00 PDT --- Ouch. That last sentence of my needs editing. I meant to say that I don't want to see structs on the _stack_ not being allowed to have destructors in SafeD. But obviously you understood what I meant. As for your suggestion, couldn't the compiler just disallow structs with destructors from anywhere but the stack in SafeD? If you try and declare them anywhere else, you'd get an error. There shouldn't be any need to distinguish them otherwise. The destructor itself could do that. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #4 from Michel Fortin michel.for...@michelf.com 2010-08-11 15:33:11 EDT --- The problem with structs is that many structs will need a destructor because they encapsulate a resource not managed by the GC. That destructor can be made GC-heap safe, and thus this struct can be put in a class. For instance, a File struct wrapping a file handle could easily be made GC-heap safe if it's destructor just calls fclose(handle), and thus could be put in a class. Are you willing to make this File struct unusable in SafeD? Or std.container.Array, which is totally safe to use on the heap too? So I think there is a need to distinguish GC-safe structs from those that aren't. Forbidding all structs with a destructor to be put on the heap in SafeD is going to prevent too many useful things. Obviously, the struct itself would need a @trusted destructor. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #5 from Jonathan M Davis jmdavisp...@gmail.com 2010-08-11 13:10:28 PDT --- This mess is just too complicated. Sigh. Well, we want to be able to use structs with destructors in SafeD wherever is reasonable to use them, and those uses should be allowed. Unsafe uses should not be allowed. If attributes of some kind are required on the structs or their destructors to make it work, then that's the path that we should take. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 4621] Destructors are inherently u...@safe
http://d.puremagic.com/issues/show_bug.cgi?id=4621 --- Comment #6 from Michel Fortin michel.for...@michelf.com 2010-08-11 16:20:44 EDT --- It could be an attribute, or it could be something else. For instance, instead of having just destructors, we could have destructors (~this) and finalizers (~~this). A struct with neither can go anywhere, a struct with a destructor but no finalizer cannot go on the GC-heap, a struct with only a finalizer can go anywhere (the finalizer is used as the destructor), and a struct with both can go anywhere. The finalizer cannot be made @safe. Doing this with structs would probably mean allowing only finalizers (~~this) for classes, which according to my syntax suggestion would break existing code for class destructors. Perhaps the syntax should be different. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---