January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On Thursday, 31 January 2013 at 19:34:29 UTC, Dmitry Olshansky wrote:
> This is fine except that it doesn't solve the problem of:
> Container a = Container(); //should be empty container, not illegal null-container
>
> Basically @dsiable this() is optional, but I like to help user avoid bugs.
You have static opCall for such things. Since when this was a problem?
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On 01/31/2013 08:34 PM, Dmitry Olshansky wrote: > 31-Jan-2013 23:32, Maxim Fomin пишет: >> On Thursday, 31 January 2013 at 19:17:56 UTC, Dmitry Olshansky wrote: >>> 31-Jan-2013 19:21, Andrei Alexandrescu пишет: >>>> On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >>>>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>>>>> As far as I can tell classes have the same problem. >>>>> >>>>> Nope. >>>>> >>>>> void foo(someclass aa, int x, int y) >>>>> { >>>>> aa[x] = y; >>>>> } >>>>> >>>>> void main() >>>>> { >>>>> someclass aa; >>>>> foo(aa, 1, 2); // segfault >>>>> ... >>>>> } >>>> >>>> We could easily arrange things to segfault just the same with a >>>> struct-based implementation. >>>> >>> >>> Structs are quite borked in this regard e.g. without extra efforts the >>> following: >>> >>> somclass aa = someclass(); >>> foor(aa, 1, 2); // segfault, surprize someclass() is someclass.init >>> >>> The current workaround I find the most sensible is: >>> - @disable this(); >>> - make all constructors private >>> - define opCall and forward it to private constructors. 0-arg versions >>> have to pass dummy and/or default values to get struct constructed >>> >>> - automate this boilerplate until something in language is fixed? :) >>> >>> The advantage is that the following is illegal: >>> someclass aa; >>> >>> and the following works as expected: >>> auto aa = someclass(); //create empty container damn it! >> >> If desired default struct value is constant for all instances of that >> struct, there is another workaround by changing init values: >> ------------------- >> import std.stdio; >> >> class A { int x = 5; } >> >> struct S { int x = 5; A a; } >> >> void main() >> { >> S s; // defaults to preallocated a, a is not null >> assert(S.x is 100 && s.a !is null && s.a.x is 100); >> } > > This is fine except that it doesn't solve the problem of: > Container a = Container(); //should be empty container, not illegal null-container Just use a payload struct and check whether that is null before every operation. > > Basically @dsiable this() is optional, but I like to help user avoid bugs. > >> >> import runtime; >> >> mixin(declareExternInitZPointer!S); >> >> static this() >> { >> A a = new A; >> a.x = 100; >> S s = S(100, a); >> rtSetDefaultHeapInitializer(s); >> rtSetDefaultStackInitializer(s, mixin(passExternInitZPointer!S)); >> } >> ---------------------- >> >> Where runtime module is http://dpaste.dzfl.pl/40b59a5d >> >> This does not fully replace default ctor, since all instances of S are >> affected. > > |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert burner Schadek | 31-Jan-2013 23:54, Robert burner Schadek пишет: > > On 01/31/2013 08:34 PM, Dmitry Olshansky wrote: >> 31-Jan-2013 23:32, Maxim Fomin пишет: >>> On Thursday, 31 January 2013 at 19:17:56 UTC, Dmitry Olshansky wrote: >>>> 31-Jan-2013 19:21, Andrei Alexandrescu пишет: >>>>> On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >>>>>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>>>>>> As far as I can tell classes have the same problem. >>>>>> >>>>>> Nope. >>>>>> >>>>>> void foo(someclass aa, int x, int y) >>>>>> { >>>>>> aa[x] = y; >>>>>> } >>>>>> >>>>>> void main() >>>>>> { >>>>>> someclass aa; >>>>>> foo(aa, 1, 2); // segfault >>>>>> ... >>>>>> } >>>>> >>>>> We could easily arrange things to segfault just the same with a >>>>> struct-based implementation. >>>>> >>>> >>>> Structs are quite borked in this regard e.g. without extra efforts the >>>> following: >>>> >>>> somclass aa = someclass(); >>>> foor(aa, 1, 2); // segfault, surprize someclass() is someclass.init >>>> >>>> The current workaround I find the most sensible is: >>>> - @disable this(); >>>> - make all constructors private >>>> - define opCall and forward it to private constructors. 0-arg versions >>>> have to pass dummy and/or default values to get struct constructed >>>> >>>> - automate this boilerplate until something in language is fixed? :) >>>> >>>> The advantage is that the following is illegal: >>>> someclass aa; >>>> >>>> and the following works as expected: >>>> auto aa = someclass(); //create empty container damn it! >>> >>> If desired default struct value is constant for all instances of that >>> struct, there is another workaround by changing init values: >>> ------------------- >>> import std.stdio; >>> >>> class A { int x = 5; } >>> >>> struct S { int x = 5; A a; } >>> >>> void main() >>> { >>> S s; // defaults to preallocated a, a is not null >>> assert(S.x is 100 && s.a !is null && s.a.x is 100); >>> } >> >> This is fine except that it doesn't solve the problem of: >> Container a = Container(); //should be empty container, not illegal >> null-container > Just use a payload struct and check whether that is null before every > operation. Lazy evaluation, great. Except that is a useless opcode per every operation with container + keep in in mind the problem: void func(cont a) { //if a was empty and we go with lazy-initialization // then the outside cont is not updated a.insert(...); } void main() { ... cont x; ... //this could be non-trivial code that //may or may not touch x in all path func(x); ... } -- Dmitry Olshansky |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | 31-Jan-2013 23:51, Maxim Fomin пишет: > On Thursday, 31 January 2013 at 19:34:29 UTC, Dmitry Olshansky wrote: >> This is fine except that it doesn't solve the problem of: >> Container a = Container(); //should be empty container, not illegal >> null-container >> >> Basically @dsiable this() is optional, but I like to help user avoid >> bugs. > > You have static opCall for such things. Since when this was a problem? Yes, that's why I use it still... it can't initialize a const struct *and* requires a T.init or T a = void; or forward to this(int dummy=42) container. -- Dmitry Olshansky |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert burner Schadek | On Thursday, 31 January 2013 at 19:55:01 UTC, Robert burner Schadek wrote:
> Just use a payload struct and check whether that is null before every operation.
The following problem is inherent to this approach:
---
struct Array(T) {
static struct Payload {
T* data;
size_t length;
}
Payload* p;
// ...
}
void fill(T)(Array!T a) {
a.length = 3;
a[0] = 1;
a[1] = 2;
a[2] = 3;
}
void client() {
Array!int a;
fill(a);
// Oops, a is still empty (with null payload).
}
---
Such containers are "almost, but not quite" reference types; reason enough to @disable this() in order to improve the user experience, in my opinion.
David
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert burner Schadek | On 1/31/13 2:54 PM, Robert burner Schadek wrote: [snip] Please avoid overquoting - this is the worst there is: one line inserted without whitespace in the middle of a long quote. Thanks, Andrei |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu |
On 31.01.2013 17:17, Andrei Alexandrescu wrote:
> On 1/31/13 11:00 AM, Steven Schveighoffer wrote:
>> On Thu, 31 Jan 2013 10:40:15 -0500, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>> It has a destructor.
>>
>> One which is not called if allocated on the heap.
>
> That is correct. My point was that with structs we get to implement
> full-fledged reference counting for containers.
>
> Reference counting makes a lot of sense for containers. They inherently
> own their internals, don't have cycles, and are massive enough to make
> pass by value effectively an anti-pattern (as it is for C++). At the
> same time memory reclamation is of high interest. Reference counting is
> really the sweet spot for containers.
I can understand interest in early and deterministic reclamation of memory, but I have some issues with reference counting. Maybe you can shed some light on these:
- how do you reference count immutable containers? You'll have to cast the payload to mutable and assume it is not in read-only memory.
- how do you do reference counting when accessing shared containers in multiple threads? Consider clearing the last reference to a shared reference counted object while another thread reads this reference. With usual atomic operations on the reference count only:
1. the reading thread reads the payload pointer
2. the writing thread reads the payload pointer, decrements the reference count and frees the array
3. the reading thread increments the reference count, but accesses the deallocated array.
With atomic operations, I can only imagine fixing this with CAS2 operations reading/changing both pointer and reference count at the same time, but even then, it is not obvious. Anyway, this operation is not supported by the currently popular processors.
In contrast, GC managed memory is memory-@safe in multi-threading environments by design.
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On Thu, 31 Jan 2013 14:22:24 -0500, Maxim Fomin <maxim@maxim-fomin.ru> wrote:
> On Thursday, 31 January 2013 at 18:59:20 UTC, Steven Schveighoffer wrote:
>> Structs by default are easily destroyed from the stack, but lack the fundamental cogs to get GC-based destruction.
>>
>> -Steve
>
> Perhaps _d_newitemT() and buddies can check whether it creates structs, store a collection of pointers to structs it allocates and at the end of program some druntime routine loops through out of the collection to run dtors?
What is needed is a precise GC, where the struct typeinfo and destructor are stored along with the block.
However, we ALSO need two functions, a destructor (Called from the stack deterministically) and a finalizer (called from the GC).
Like I said, this isn't an unsolvable problem, but neither is making classes reference counted.
-Steve
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rainer Schuetze | On Thu, 31 Jan 2013 15:32:23 -0500, Rainer Schuetze <r.sagitario@gmx.de> wrote: > > > On 31.01.2013 17:17, Andrei Alexandrescu wrote: >> On 1/31/13 11:00 AM, Steven Schveighoffer wrote: >>> On Thu, 31 Jan 2013 10:40:15 -0500, Andrei Alexandrescu >>> <SeeWebsiteForEmail@erdani.org> wrote: >>>> It has a destructor. >>> >>> One which is not called if allocated on the heap. >> >> That is correct. My point was that with structs we get to implement >> full-fledged reference counting for containers. >> >> Reference counting makes a lot of sense for containers. They inherently >> own their internals, don't have cycles, and are massive enough to make >> pass by value effectively an anti-pattern (as it is for C++). At the >> same time memory reclamation is of high interest. Reference counting is >> really the sweet spot for containers. > > I can understand interest in early and deterministic reclamation of memory, but I have some issues with reference counting. Maybe you can shed some light on these: > > - how do you reference count immutable containers? You'll have to cast the payload to mutable and assume it is not in read-only memory. The reference count part is not immutable. e.g.: struct refcountstruct { int count; immutable(containerimpl) impl; } > > - how do you do reference counting when accessing shared containers in multiple threads? Consider clearing the last reference to a shared reference counted object while another thread reads this reference. With usual atomic operations on the reference count only: > > 1. the reading thread reads the payload pointer > 2. the writing thread reads the payload pointer, decrements the reference count and frees the array > 3. the reading thread increments the reference count, but accesses the deallocated array. I would be concerned if a thread can get ahold of a reference counted pointer without having the counter incremented already on its behalf. -Steve |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 31.01.2013 21:48, Steven Schveighoffer wrote: > On Thu, 31 Jan 2013 15:32:23 -0500, Rainer Schuetze <r.sagitario@gmx.de> > wrote: > >> - how do you reference count immutable containers? You'll have to cast >> the payload to mutable and assume it is not in read-only memory. > > The reference count part is not immutable. > > e.g.: > > struct refcountstruct > { > int count; > immutable(containerimpl) impl; > } What about immutable(refcountstruct)? Maybe only implicitely as field of an immutable class. > >> >> - how do you do reference counting when accessing shared containers in >> multiple threads? Consider clearing the last reference to a shared >> reference counted object while another thread reads this reference. >> With usual atomic operations on the reference count only: >> >> 1. the reading thread reads the payload pointer >> 2. the writing thread reads the payload pointer, decrements the >> reference count and frees the array >> 3. the reading thread increments the reference count, but accesses >> the deallocated array. > > I would be concerned if a thread can get ahold of a reference counted > pointer without having the counter incremented already on its behalf. > You have to read the pointer to increment the counter to get hold of the reference. |
Copyright © 1999-2021 by the D Language Foundation