Jump to page: 1 2
Thread overview
The "@safe vs struct destructor" dilemma
Apr 11, 2014
Nick Sabalausky
Apr 11, 2014
Kagamin
Apr 11, 2014
Michel Fortin
Apr 11, 2014
Nick Sabalausky
Apr 12, 2014
Michel Fortin
Apr 12, 2014
Marc Schütz
Apr 12, 2014
Michel Fortin
Apr 12, 2014
deadalnix
Apr 12, 2014
Michel Fortin
Apr 12, 2014
Kagamin
Apr 12, 2014
Michel Fortin
Apr 12, 2014
Kagamin
Apr 12, 2014
Timon Gehr
Apr 12, 2014
Michel Fortin
Apr 12, 2014
deadalnix
Apr 12, 2014
Nick Sabalausky
Apr 12, 2014
Dicebot
Apr 13, 2014
Nick Sabalausky
Apr 13, 2014
Dicebot
April 11, 2014
So the idea behind @safe is most code should be @safe, with occasional @system/@trusted pieces isolated deeper in the call chain. That inevitably means occasionally invoking @system from @safe via an @trusted intermediary.

Realistically, I would imagine this @trusted part should *always* be a dummy wrapper over a specific @system function. Why? Because @trusted disables ALL of @safe's extra safety checks. Therefore, restricting usage of @trusted to ONLY be dummy wrappers over the specific parts which MUST be  @system will minimize the amount of collateral code that must loose all of @safe's special safety checks.

This means some mildly-annoying boilerplate at all the @safe -> @system seams, but it's doable...*EXCEPT*, afaics, for struct destructors. Maybe I'm missing something, but I'm not aware of any reasonable way to stuff those behind an @trusted wrapper (or even an ugly way, for that matter).

If there really *isn't* a reasonable way to wrap @system struct destructors (ex: RefCounted) inside an @trusted wall, then any such structs will poison all functions which touch them into being @trusted, thus destroying the @safe safety checks for the *entire* body of such functions. Well, that is, aside from any portions of the function which don't touch the struct *and* can be factored out into separate @safe helper functions - but that solution seems both limited and contortion-prone.

Any thoughts?
April 11, 2014
RefCounted is probably unsafe, so just don't use it in safe code.
April 11, 2014
On 2014-04-11 06:29:32 +0000, Nick Sabalausky <SeeWebsiteToContactMe@semitwist.com> said:

> So the idea behind @safe is most code should be @safe, with occasional @system/@trusted pieces isolated deeper in the call chain. That inevitably means occasionally invoking @system from @safe via an @trusted intermediary.
> 
> Realistically, I would imagine this @trusted part should *always* be a dummy wrapper over a specific @system function. Why? Because @trusted disables ALL of @safe's extra safety checks. Therefore, restricting usage of @trusted to ONLY be dummy wrappers over the specific parts which MUST be  @system will minimize the amount of collateral code that must loose all of @safe's special safety checks.
> 
> This means some mildly-annoying boilerplate at all the @safe -> @system seams, but it's doable...*EXCEPT*, afaics, for struct destructors. Maybe I'm missing something, but I'm not aware of any reasonable way to stuff those behind an @trusted wrapper (or even an ugly way, for that matter).
> 
> If there really *isn't* a reasonable way to wrap @system struct destructors (ex: RefCounted) inside an @trusted wall, then any such structs will poison all functions which touch them into being @trusted, thus destroying the @safe safety checks for the *entire* body of such functions. Well, that is, aside from any portions of the function which don't touch the struct *and* can be factored out into separate @safe helper functions - but that solution seems both limited and contortion-prone.
> 
> Any thoughts?

Can destructors be @safe at all? When called from the GC the destructor 1) likely runs in a different thread and 2) can potentially access other destructed objects, those objects might contain pointers to deallocated memory if their destructor manually freed a memory block.

-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca

April 11, 2014
On 4/11/2014 3:54 PM, Michel Fortin wrote:
>
> Can destructors be @safe at all? When called from the GC the destructor
> 1) likely runs in a different thread and 2) can potentially access other
> destructed objects, those objects might contain pointers to deallocated
> memory if their destructor manually freed a memory block.
>

If destructors can't be @safe, that would seem to create a fairly sizable hole in the utility of @safe.

April 12, 2014
On 2014-04-11 22:22:18 +0000, Nick Sabalausky <SeeWebsiteToContactMe@semitwist.com> said:

> On 4/11/2014 3:54 PM, Michel Fortin wrote:
>> 
>> Can destructors be @safe at all? When called from the GC the destructor
>> 1) likely runs in a different thread and 2) can potentially access other
>> destructed objects, those objects might contain pointers to deallocated
>> memory if their destructor manually freed a memory block.
> 
> If destructors can't be @safe, that would seem to create a fairly sizable hole in the utility of @safe.

Well, they are safe as long as they're not called by the GC. I think you could make them safe even with the GC by changing things this way:

1- make the GC call the destructor in the same thread the object was created in (for non-shared objects), so any access to thread-local stuff stays in the right thread, avoiding low-level races.

2- after the destructor is run on an object, wipe out the memory block with zeros. This way if another to-be-destructed object has a pointer to it, at worse it'll dereference a null pointer. With this you might get a sporadic crash when it happens, but that's better than memory corruption. You only need to do this when allocated on the GC heap, and only pointers need to be zeroed, and only if another object being destroyed is still pointing to this object, and perhaps only do it for @safe destructors.

-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca

April 12, 2014
On Fri, 11 Apr 2014 23:02:55 -0400, Michel Fortin <michel.fortin@michelf.ca> wrote:

> Well, they are safe as long as they're not called by the GC. I think you could make them safe even with the GC by changing things this way:
>
> 1- make the GC call the destructor in the same thread the object was created in (for non-shared objects), so any access to thread-local stuff stays in the right thread, avoiding low-level races.

This needs to be done sooner rather than later. It would solve a lot of GC annoyances.

I think in the ARC discussion, it also came up as a necessary step.

-Steve
April 12, 2014
On Saturday, 12 April 2014 at 03:02:56 UTC, Michel Fortin wrote:
> 1- make the GC call the destructor in the same thread the object was created in (for non-shared objects), so any access to thread-local stuff stays in the right thread, avoiding low-level races.

There also needs to be a mechanism to promote a local object to shared (and probably vice versa). This can be easily done with unique objects, although depending on the implementation, it would require moving memory.

>
> 2- after the destructor is run on an object, wipe out the memory block with zeros. This way if another to-be-destructed object has a pointer to it, at worse it'll dereference a null pointer. With this you might get a sporadic crash when it happens, but that's better than memory corruption. You only need to do this when allocated on the GC heap, and only pointers need to be zeroed, and only if another object being destroyed is still pointing to this object, and perhaps only do it for @safe destructors.

More correctly, every reference to the destroyed object needs to be wiped, not the object itself. But this requires a fully precise GC.
April 12, 2014
On Friday, 11 April 2014 at 06:29:39 UTC, Nick Sabalausky wrote:
> Realistically, I would imagine this @trusted part should *always* be a dummy wrapper over a specific @system function. Why? Because @trusted disables ALL of @safe's extra safety checks. Therefore, restricting usage of @trusted to ONLY be dummy wrappers over the specific parts which MUST be  @system will minimize the amount of collateral code that must loose all of @safe's special safety checks.
>

No.

Trusted is about providing a safe interface to some unsafe internals. For instance, free cannot be safe. But a function can do malloc and free in a safe manner. That function can thus be tagged @trusted .

When you tag something @trusted, you are telling that the part aren't individually proven to be safe, but the develloper ensured that the whole, as seen from outside, is safe.

The thin wrapper thing do not really fit that model.

> If there really *isn't* a reasonable way to wrap @system struct destructors (ex: RefCounted) inside an @trusted wall, then any such structs will poison all functions which touch them into being @trusted, thus destroying the @safe safety checks for the *entire* body of such functions. Well, that is, aside from any portions of the function which don't touch the struct *and* can be factored out into separate @safe helper functions - but that solution seems both limited and contortion-prone.
>
> Any thoughts?

RefCounted can't be made safe in any way given the current type system.
April 12, 2014
On Saturday, 12 April 2014 at 03:02:56 UTC, Michel Fortin wrote:
> 2- after the destructor is run on an object, wipe out the memory block with zeros. This way if another to-be-destructed object has a pointer to it, at worse it'll dereference a null pointer. With this you might get a sporadic crash when it happens, but that's better than memory corruption. You only need to do this when allocated on the GC heap, and only pointers need to be zeroed, and only if another object being destroyed is still pointing to this object, and perhaps only do it for @safe destructors.

You don't get a crash, you get undefined behavior. That is much worse and certainly not @safe.
April 12, 2014
On Saturday, 12 April 2014 at 03:02:56 UTC, Michel Fortin wrote:
> 2- after the destructor is run on an object, wipe out the memory block with zeros. This way if another to-be-destructed object has a pointer to it, at worse it'll dereference a null pointer. With this you might get a sporadic crash when it happens, but that's better than memory corruption.

Other objects will have a valid pointer to zeroed out block and will be able to call its methods. They are likely to crash, but it's not guaranteed, they may just fine corrupt memory. Imagine the class has a pointer to a memory block of 10MB size, the size is an enum and is encoded in the function code (won't be zeroed), the function may write to any region of that block of memory pointed to by null after the clearing.
« First   ‹ Prev
1 2