Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
June 27, 2005 Reference counting | ||||
---|---|---|---|---|
| ||||
I got little or no comment on this idea/code I posted to another (rather large) thread here, I figured I'd post it again and see what people thought. After reading this thread (the one I mention above) and also this one Andrew dug up: http://www.digitalmars.com/d/archives/7988.html It appears you need a type that: 1 - is stack based. 2 - has deterministic destruction. 3 - has opAssign. It seems (from recent posts) that Walter plans to put auto classes on the stack, so they'd then fulfil requirements 1 and 2. But what about 3? To me, opAssign isn't generally a good idea for classes (assigning a value to a reference seems illogical) but would be a good for structs (they're value types, assignment assigns a value). Can you do reference counting without opAssign? The idea for D (or so I've read) is that opAssign isn't required, instead you use a copy constructor. Now, I've little experience with ref counting, or with writing an implementation of one, but, does this work? If not, why not? What's missing, what else is required? Overload of '.' perhaps? The ability to pass a RefPtr as the object being counted? import std.c.windows.windows; import std.random; import std.thread; import std.stdio; class RefPtr { RefPtr parent; Object resource = null; int refs = 0; this(Object res) { resource = res; writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for resource=(",resource,")"); increment(); } this(RefPtr rhs) { parent = rhs; parent.increment(); writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for resource=(",parent.resource,")"); } ~this() { int r; if ((r = decrement()) == 0) { writefln("ThreadID=(",Thread.getThis().id,") release last ref Ref=(",r,")"); if (parent) parent = null; else if (resource) { writefln("ThreadID=(",Thread.getThis().id,") delete resource=(",resource,")"); delete resource; resource = null; } } writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")"); } protected: int increment() { int ret; if (parent) ret = parent.increment(); else { synchronized(this) { ret = ++refs; writefln("ThreadID=(",Thread.getThis().id,") increment to Ref=(",refs,")"); } } return ret; } int decrement() { int ret; if (parent) ret = parent.decrement(); else { synchronized(this) { ret = --refs; writefln("ThreadID=(",Thread.getThis().id,") decrement to Ref=(",refs,")"); } } return ret; } } class Resource { char[] name = "11 was a racehorse"; char[] toString() { return name; } } RefPtr pbob; static this() { pbob = new RefPtr(new Resource()); } static ~this() { delete pbob; } void main() { Thread[] threads; int i; writefln("ThreadID=(",Thread.getThis().id,") is the main thread"); for(i = 0; i < 10; i++) { threads ~= new Thread(&thread_function,null); threads[$-1].start(); } while(true) { i = 0; foreach(Thread t; threads) { if (t.getState() == Thread.TS.TERMINATED) i++; } if (i == 10) break; Sleep(100); } writefln("Main exiting"); } int thread_function(void* isnull) { auto RefPtr p = new RefPtr(pbob); Sleep(1000+rand()%1000); return 0; } Regan |
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Reagan, let's take a look on more simple case - implementation of 'auto' pointer (this is simplifed version) struct Auto(X) { // constructor. this( X _ptr = 0 ) { ptr = _ptr; owner = true; } // the destructor deletes ptr only if we are the owner. ~this() { if( owner && ptr ) delete ptr; } // the assignment operator. void opAssign( X r ) { if( owner && ptr ) delete ptr; ptr = r; owner = true; } // the assignment operator. transfer // ownership. (not full implementation, sic) void opAssign( Auto r ) { if( owner && ptr ) delete ptr; ptr = r.release(); owner = true; } X value() { return ptr; } // release returns the ptr value and releases ownership // if we were previously the owner. X release() { owner = false; return value(); } private X ptr; private bool owner; } ------------------------- { Auto!(Object) autop = new Object(); // first instance autop = new Object(); // first instance will be deleted; // and autop contains second instance. } // second instance will be deleted here // even in case of exception. This is basic auto_ptr used in C++ and it simplfies life a lot. opAssign and dtors are crucial here. And such Auto(X) must be a struct. Classes do not work here. Andrew. "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0boj0r23k2f5@nrage.netwin.co.nz... >I got little or no comment on this idea/code I posted to another (rather large) thread here, I figured I'd post it again and see what people thought. > > After reading this thread (the one I mention above) and also this one > Andrew dug up: > http://www.digitalmars.com/d/archives/7988.html > > It appears you need a type that: > > 1 - is stack based. > 2 - has deterministic destruction. > 3 - has opAssign. > > It seems (from recent posts) that Walter plans to put auto classes on the stack, so they'd then fulfil requirements 1 and 2. > > But what about 3? To me, opAssign isn't generally a good idea for classes (assigning a value to a reference seems illogical) but would be a good for structs (they're value types, assignment assigns a value). > > Can you do reference counting without opAssign? > > The idea for D (or so I've read) is that opAssign isn't required, instead you use a copy constructor. Now, I've little experience with ref counting, or with writing an implementation of one, but, does this work? > > If not, why not? What's missing, what else is required? Overload of '.' perhaps? The ability to pass a RefPtr as the object being counted? > > import std.c.windows.windows; > import std.random; > import std.thread; > import std.stdio; > > class RefPtr > { > RefPtr parent; > Object resource = null; > int refs = 0; > > this(Object res) > { > resource = res; > writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for > resource=(",resource,")"); > increment(); } > > this(RefPtr rhs) > { > parent = rhs; > parent.increment(); > writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for > resource=(",parent.resource,")"); > } > > ~this() > { > int r; > > if ((r = decrement()) == 0) > { > writefln("ThreadID=(",Thread.getThis().id,") release last ref > Ref=(",r,")"); > if (parent) parent = null; > else if (resource) > { > writefln("ThreadID=(",Thread.getThis().id,") delete > resource=(",resource,")"); > delete resource; > resource = null; > } > } > writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")"); > } > protected: > int increment() > { > int ret; > > if (parent) ret = parent.increment(); > else { > synchronized(this) > { > ret = ++refs; > writefln("ThreadID=(",Thread.getThis().id,") increment to > Ref=(",refs,")"); > } > } > > return ret; > } > int decrement() > { > int ret; > > if (parent) ret = parent.decrement(); > else { > synchronized(this) > { > ret = --refs; > writefln("ThreadID=(",Thread.getThis().id,") decrement to > Ref=(",refs,")"); > } > } > > return ret; > } > } > > class Resource > { > char[] name = "11 was a racehorse"; > char[] toString() { return name; } > } > > RefPtr pbob; > > static this() > { > pbob = new RefPtr(new Resource()); > } > > static ~this() > { > delete pbob; > } > > void main() > { > Thread[] threads; > int i; > > writefln("ThreadID=(",Thread.getThis().id,") is the main thread"); > > for(i = 0; i < 10; i++) > { > threads ~= new Thread(&thread_function,null); > threads[$-1].start(); > } > > while(true) > { > i = 0; > foreach(Thread t; threads) > { > if (t.getState() == Thread.TS.TERMINATED) i++; > } > if (i == 10) break; > Sleep(100); > } > > writefln("Main exiting"); > } > > int thread_function(void* isnull) > { > auto RefPtr p = new RefPtr(pbob); > Sleep(1000+rand()%1000); return 0; > } > > Regan |
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | Looking at the code given I believe my code sample can be modified to achieve it. opAssign isn't required, you simply use a copy constructor (as I have done). Why can't it be a class? Specifically why can't it be an 'auto' class reference (which will exist on the stack in future)?
Regan
p.s. It's "Regan" not "Reagan" ;)
On Sun, 26 Jun 2005 21:39:48 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote:
> Reagan, let's take a look on more simple case - implementation
> of 'auto' pointer (this is simplifed version)
>
> struct Auto(X)
> {
>
> // constructor.
> this( X _ptr = 0 ) { ptr = _ptr; owner = true; }
>
> // the destructor deletes ptr only if we are the owner.
> ~this() { if( owner && ptr ) delete ptr; }
>
> // the assignment operator.
> void opAssign( X r )
> {
> if( owner && ptr ) delete ptr;
> ptr = r;
> owner = true;
> }
>
> // the assignment operator. transfer
> // ownership. (not full implementation, sic)
> void opAssign( Auto r )
> {
> if( owner && ptr ) delete ptr;
> ptr = r.release();
> owner = true;
> }
>
> X value() { return ptr; }
>
> // release returns the ptr value and releases ownership
> // if we were previously the owner.
>
> X release()
> {
> owner = false;
> return value();
> }
> private X ptr;
> private bool owner;
> }
>
> -------------------------
>
> {
> Auto!(Object) autop = new Object(); // first instance
> autop = new Object(); // first instance will be deleted;
> // and autop contains second
> instance.
> } // second instance will be deleted here
> // even in case of exception.
>
> This is basic auto_ptr used in C++
> and it simplfies life a lot.
> opAssign and dtors are crucial here.
> And such Auto(X) must be a struct.
> Classes do not work here.
>
> Andrew.
>
>
> "Regan Heath" <regan@netwin.co.nz> wrote in message
> news:opss0boj0r23k2f5@nrage.netwin.co.nz...
>> I got little or no comment on this idea/code I posted to another (rather
>> large) thread here, I figured I'd post it again and see what people
>> thought.
>>
>> After reading this thread (the one I mention above) and also this one
>> Andrew dug up:
>> http://www.digitalmars.com/d/archives/7988.html
>>
>> It appears you need a type that:
>>
>> 1 - is stack based.
>> 2 - has deterministic destruction.
>> 3 - has opAssign.
>>
>> It seems (from recent posts) that Walter plans to put auto classes on the
>> stack, so they'd then fulfil requirements 1 and 2.
>>
>> But what about 3? To me, opAssign isn't generally a good idea for classes
>> (assigning a value to a reference seems illogical) but would be a good for
>> structs (they're value types, assignment assigns a value).
>>
>> Can you do reference counting without opAssign?
>>
>> The idea for D (or so I've read) is that opAssign isn't required, instead
>> you use a copy constructor. Now, I've little experience with ref counting,
>> or with writing an implementation of one, but, does this work?
>>
>> If not, why not? What's missing, what else is required? Overload of '.'
>> perhaps? The ability to pass a RefPtr as the object being counted?
>>
>> import std.c.windows.windows;
>> import std.random;
>> import std.thread;
>> import std.stdio;
>>
>> class RefPtr
>> {
>> RefPtr parent;
>> Object resource = null;
>> int refs = 0;
>>
>> this(Object res)
>> {
>> resource = res;
>> writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for
>> resource=(",resource,")");
>> increment(); }
>>
>> this(RefPtr rhs)
>> {
>> parent = rhs;
>> parent.increment();
>> writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for
>> resource=(",parent.resource,")");
>> }
>>
>> ~this()
>> {
>> int r;
>>
>> if ((r = decrement()) == 0)
>> {
>> writefln("ThreadID=(",Thread.getThis().id,") release last ref
>> Ref=(",r,")");
>> if (parent) parent = null;
>> else if (resource)
>> {
>> writefln("ThreadID=(",Thread.getThis().id,") delete
>> resource=(",resource,")");
>> delete resource;
>> resource = null;
>> }
>> }
>> writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")");
>> }
>> protected:
>> int increment()
>> {
>> int ret;
>>
>> if (parent) ret = parent.increment();
>> else {
>> synchronized(this)
>> {
>> ret = ++refs;
>> writefln("ThreadID=(",Thread.getThis().id,") increment to
>> Ref=(",refs,")");
>> }
>> }
>>
>> return ret;
>> }
>> int decrement()
>> {
>> int ret;
>>
>> if (parent) ret = parent.decrement();
>> else {
>> synchronized(this)
>> {
>> ret = --refs;
>> writefln("ThreadID=(",Thread.getThis().id,") decrement to
>> Ref=(",refs,")");
>> }
>> }
>>
>> return ret;
>> }
>> }
>>
>> class Resource
>> {
>> char[] name = "11 was a racehorse";
>> char[] toString() { return name; }
>> }
>>
>> RefPtr pbob;
>>
>> static this()
>> {
>> pbob = new RefPtr(new Resource());
>> }
>>
>> static ~this()
>> {
>> delete pbob;
>> }
>>
>> void main()
>> {
>> Thread[] threads;
>> int i;
>>
>> writefln("ThreadID=(",Thread.getThis().id,") is the main thread");
>>
>> for(i = 0; i < 10; i++)
>> {
>> threads ~= new Thread(&thread_function,null);
>> threads[$-1].start();
>> }
>>
>> while(true)
>> {
>> i = 0;
>> foreach(Thread t; threads)
>> {
>> if (t.getState() == Thread.TS.TERMINATED) i++;
>> }
>> if (i == 10) break;
>> Sleep(100);
>> }
>>
>> writefln("Main exiting");
>> }
>>
>> int thread_function(void* isnull)
>> {
>> auto RefPtr p = new RefPtr(pbob);
>> Sleep(1000+rand()%1000); return 0;
>> }
>>
>> Regan
>
>
|
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | AutoPtr in D...
import std.c.windows.windows;
import std.random;
import std.thread;
import std.stdio;
class AutoPtr
{
Object resource = null;
bool owner;
this(Object rhs)
{
resource = rhs;
owner = true;
writefln("%x constructed for %x",this,resource);
}
this(AutoPtr rhs)
{
resource = rhs.release();
owner = true;
writefln("%x obtaining %x from %x",this,resource,rhs);
}
~this()
{
if (resource && owner)
{
writefln("%x deleting %x",this,resource);
delete resource;
resource = null;
}
}
protected:
Object value()
{
return resource;
}
Object release()
{
writefln("%x releasing %x",this,resource);
owner = false;
return value();
}
}
class Resource
{
char[] name = "11 was a racehorse";
char[] toString() { return name; }
}
void main()
{
Resource Bob = new Resource();
auto AutoPtr p = new AutoPtr(Bob);
auto AutoPtr q = new AutoPtr(p);
writefln("end of main");
}
On Mon, 27 Jun 2005 16:52:09 +1200, Regan Heath <regan@netwin.co.nz> wrote:
> Looking at the code given I believe my code sample can be modified to achieve it. opAssign isn't required, you simply use a copy constructor (as I have done). Why can't it be a class? Specifically why can't it be an 'auto' class reference (which will exist on the stack in future)?
>
> Regan
>
> p.s. It's "Regan" not "Reagan" ;)
>
> On Sun, 26 Jun 2005 21:39:48 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote:
>> Reagan, let's take a look on more simple case - implementation
>> of 'auto' pointer (this is simplifed version)
>>
>> struct Auto(X)
>> {
>>
>> // constructor.
>> this( X _ptr = 0 ) { ptr = _ptr; owner = true; }
>>
>> // the destructor deletes ptr only if we are the owner.
>> ~this() { if( owner && ptr ) delete ptr; }
>>
>> // the assignment operator.
>> void opAssign( X r )
>> {
>> if( owner && ptr ) delete ptr;
>> ptr = r;
>> owner = true;
>> }
>>
>> // the assignment operator. transfer
>> // ownership. (not full implementation, sic)
>> void opAssign( Auto r )
>> {
>> if( owner && ptr ) delete ptr;
>> ptr = r.release();
>> owner = true;
>> }
>>
>> X value() { return ptr; }
>>
>> // release returns the ptr value and releases ownership
>> // if we were previously the owner.
>>
>> X release()
>> {
>> owner = false;
>> return value();
>> }
>> private X ptr;
>> private bool owner;
>> }
>>
>> -------------------------
>>
>> {
>> Auto!(Object) autop = new Object(); // first instance
>> autop = new Object(); // first instance will be deleted;
>> // and autop contains second
>> instance.
>> } // second instance will be deleted here
>> // even in case of exception.
>>
>> This is basic auto_ptr used in C++
>> and it simplfies life a lot.
>> opAssign and dtors are crucial here.
>> And such Auto(X) must be a struct.
>> Classes do not work here.
>>
>> Andrew.
>>
>>
>> "Regan Heath" <regan@netwin.co.nz> wrote in message
>> news:opss0boj0r23k2f5@nrage.netwin.co.nz...
>>> I got little or no comment on this idea/code I posted to another (rather
>>> large) thread here, I figured I'd post it again and see what people
>>> thought.
>>>
>>> After reading this thread (the one I mention above) and also this one
>>> Andrew dug up:
>>> http://www.digitalmars.com/d/archives/7988.html
>>>
>>> It appears you need a type that:
>>>
>>> 1 - is stack based.
>>> 2 - has deterministic destruction.
>>> 3 - has opAssign.
>>>
>>> It seems (from recent posts) that Walter plans to put auto classes on the
>>> stack, so they'd then fulfil requirements 1 and 2.
>>>
>>> But what about 3? To me, opAssign isn't generally a good idea for classes
>>> (assigning a value to a reference seems illogical) but would be a good for
>>> structs (they're value types, assignment assigns a value).
>>>
>>> Can you do reference counting without opAssign?
>>>
>>> The idea for D (or so I've read) is that opAssign isn't required, instead
>>> you use a copy constructor. Now, I've little experience with ref counting,
>>> or with writing an implementation of one, but, does this work?
>>>
>>> If not, why not? What's missing, what else is required? Overload of '.'
>>> perhaps? The ability to pass a RefPtr as the object being counted?
>>>
>>> import std.c.windows.windows;
>>> import std.random;
>>> import std.thread;
>>> import std.stdio;
>>>
>>> class RefPtr
>>> {
>>> RefPtr parent;
>>> Object resource = null;
>>> int refs = 0;
>>>
>>> this(Object res)
>>> {
>>> resource = res;
>>> writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for
>>> resource=(",resource,")");
>>> increment(); }
>>>
>>> this(RefPtr rhs)
>>> {
>>> parent = rhs;
>>> parent.increment();
>>> writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for
>>> resource=(",parent.resource,")");
>>> }
>>>
>>> ~this()
>>> {
>>> int r;
>>>
>>> if ((r = decrement()) == 0)
>>> {
>>> writefln("ThreadID=(",Thread.getThis().id,") release last ref
>>> Ref=(",r,")");
>>> if (parent) parent = null;
>>> else if (resource)
>>> {
>>> writefln("ThreadID=(",Thread.getThis().id,") delete
>>> resource=(",resource,")");
>>> delete resource;
>>> resource = null;
>>> }
>>> }
>>> writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")");
>>> }
>>> protected:
>>> int increment()
>>> {
>>> int ret;
>>>
>>> if (parent) ret = parent.increment();
>>> else {
>>> synchronized(this)
>>> {
>>> ret = ++refs;
>>> writefln("ThreadID=(",Thread.getThis().id,") increment to
>>> Ref=(",refs,")");
>>> }
>>> }
>>>
>>> return ret;
>>> }
>>> int decrement()
>>> {
>>> int ret;
>>>
>>> if (parent) ret = parent.decrement();
>>> else {
>>> synchronized(this)
>>> {
>>> ret = --refs;
>>> writefln("ThreadID=(",Thread.getThis().id,") decrement to
>>> Ref=(",refs,")");
>>> }
>>> }
>>>
>>> return ret;
>>> }
>>> }
>>>
>>> class Resource
>>> {
>>> char[] name = "11 was a racehorse";
>>> char[] toString() { return name; }
>>> }
>>>
>>> RefPtr pbob;
>>>
>>> static this()
>>> {
>>> pbob = new RefPtr(new Resource());
>>> }
>>>
>>> static ~this()
>>> {
>>> delete pbob;
>>> }
>>>
>>> void main()
>>> {
>>> Thread[] threads;
>>> int i;
>>>
>>> writefln("ThreadID=(",Thread.getThis().id,") is the main thread");
>>>
>>> for(i = 0; i < 10; i++)
>>> {
>>> threads ~= new Thread(&thread_function,null);
>>> threads[$-1].start();
>>> }
>>>
>>> while(true)
>>> {
>>> i = 0;
>>> foreach(Thread t; threads)
>>> {
>>> if (t.getState() == Thread.TS.TERMINATED) i++;
>>> }
>>> if (i == 10) break;
>>> Sleep(100);
>>> }
>>>
>>> writefln("Main exiting");
>>> }
>>>
>>> int thread_function(void* isnull)
>>> {
>>> auto RefPtr p = new RefPtr(pbob);
>>> Sleep(1000+rand()%1000); return 0;
>>> }
>>>
>>> Regan
>>
>>
>
|
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0is0r223k2f5@nrage.netwin.co.nz... > AutoPtr in D... > > import std.c.windows.windows; > import std.random; > import std.thread; > import std.stdio; > > class AutoPtr > { > Object resource = null; > bool owner; > > this(Object rhs) > { > resource = rhs; > owner = true; > writefln("%x constructed for %x",this,resource); > } > > this(AutoPtr rhs) > { resource = rhs.release(); > owner = true; > writefln("%x obtaining %x from %x",this,resource,rhs); > } > > ~this() > { > if (resource && owner) > { > writefln("%x deleting %x",this,resource); > delete resource; > resource = null; > } } > protected: > Object value() > { > return resource; > } > > Object release() > { > writefln("%x releasing %x",this,resource); > owner = false; > return value(); > } > } > > class Resource > { > char[] name = "11 was a racehorse"; > char[] toString() { return name; } > } > > void main() > { > Resource Bob = new Resource(); > auto AutoPtr p = new AutoPtr(Bob); > auto AutoPtr q = new AutoPtr(p); > writefln("end of main"); > } void main() { Resource Bob = new Resource(); Resource Mary = new Resource(); auto AutoPtr p = new AutoPtr(Bob); auto AutoPtr q; p = Mary; // What will happen here? q = p; // And here? } In D, for classes, q = p means assignment of reference and not value this is why classes do not work here. Having opAssign implemented allows to implement control of assigning values - to catch moment when *variable* is getting new value. And there are many useful implications of this feature. The Auto is just one example. > > On Mon, 27 Jun 2005 16:52:09 +1200, Regan Heath <regan@netwin.co.nz> wrote: >> Looking at the code given I believe my code sample can be modified to achieve it. opAssign isn't required, you simply use a copy constructor (as I have done). Why can't it be a class? Specifically why can't it be an 'auto' class reference (which will exist on the stack in future)? >> >> Regan >> >> p.s. It's "Regan" not "Reagan" ;) Ooops, terribly sorry :))) >> >> On Sun, 26 Jun 2005 21:39:48 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: >>> Reagan, let's take a look on more simple case - implementation of 'auto' pointer (this is simplifed version) >>> >>> struct Auto(X) >>> { >>> >>> // constructor. >>> this( X _ptr = 0 ) { ptr = _ptr; owner = true; } >>> >>> // the destructor deletes ptr only if we are the owner. >>> ~this() { if( owner && ptr ) delete ptr; } >>> >>> // the assignment operator. >>> void opAssign( X r ) >>> { >>> if( owner && ptr ) delete ptr; >>> ptr = r; >>> owner = true; >>> } >>> >>> // the assignment operator. transfer >>> // ownership. (not full implementation, sic) >>> void opAssign( Auto r ) >>> { >>> if( owner && ptr ) delete ptr; >>> ptr = r.release(); >>> owner = true; >>> } >>> >>> X value() { return ptr; } >>> >>> // release returns the ptr value and releases ownership >>> // if we were previously the owner. >>> >>> X release() >>> { >>> owner = false; >>> return value(); >>> } >>> private X ptr; >>> private bool owner; >>> } >>> >>> ------------------------- >>> >>> { >>> Auto!(Object) autop = new Object(); // first instance >>> autop = new Object(); // first instance will be deleted; >>> // and autop contains second >>> instance. >>> } // second instance will be deleted here >>> // even in case of exception. >>> >>> This is basic auto_ptr used in C++ >>> and it simplfies life a lot. >>> opAssign and dtors are crucial here. >>> And such Auto(X) must be a struct. >>> Classes do not work here. >>> >>> Andrew. >>> >>> >>> "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0boj0r23k2f5@nrage.netwin.co.nz... >>>> I got little or no comment on this idea/code I posted to another >>>> (rather >>>> large) thread here, I figured I'd post it again and see what people >>>> thought. >>>> >>>> After reading this thread (the one I mention above) and also this one >>>> Andrew dug up: >>>> http://www.digitalmars.com/d/archives/7988.html >>>> >>>> It appears you need a type that: >>>> >>>> 1 - is stack based. >>>> 2 - has deterministic destruction. >>>> 3 - has opAssign. >>>> >>>> It seems (from recent posts) that Walter plans to put auto classes on >>>> the >>>> stack, so they'd then fulfil requirements 1 and 2. >>>> >>>> But what about 3? To me, opAssign isn't generally a good idea for >>>> classes >>>> (assigning a value to a reference seems illogical) but would be a good >>>> for >>>> structs (they're value types, assignment assigns a value). >>>> >>>> Can you do reference counting without opAssign? >>>> >>>> The idea for D (or so I've read) is that opAssign isn't required, >>>> instead >>>> you use a copy constructor. Now, I've little experience with ref >>>> counting, >>>> or with writing an implementation of one, but, does this work? >>>> >>>> If not, why not? What's missing, what else is required? Overload of '.' perhaps? The ability to pass a RefPtr as the object being counted? >>>> >>>> import std.c.windows.windows; >>>> import std.random; >>>> import std.thread; >>>> import std.stdio; >>>> >>>> class RefPtr >>>> { >>>> RefPtr parent; >>>> Object resource = null; >>>> int refs = 0; >>>> >>>> this(Object res) >>>> { >>>> resource = res; >>>> writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for >>>> resource=(",resource,")"); >>>> increment(); } >>>> >>>> this(RefPtr rhs) >>>> { >>>> parent = rhs; >>>> parent.increment(); >>>> writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for >>>> resource=(",parent.resource,")"); >>>> } >>>> >>>> ~this() >>>> { >>>> int r; >>>> >>>> if ((r = decrement()) == 0) >>>> { >>>> writefln("ThreadID=(",Thread.getThis().id,") release last ref >>>> Ref=(",r,")"); >>>> if (parent) parent = null; >>>> else if (resource) >>>> { >>>> writefln("ThreadID=(",Thread.getThis().id,") delete >>>> resource=(",resource,")"); >>>> delete resource; >>>> resource = null; >>>> } >>>> } >>>> writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")"); >>>> } >>>> protected: >>>> int increment() >>>> { >>>> int ret; >>>> >>>> if (parent) ret = parent.increment(); >>>> else { >>>> synchronized(this) >>>> { >>>> ret = ++refs; >>>> writefln("ThreadID=(",Thread.getThis().id,") increment to >>>> Ref=(",refs,")"); >>>> } >>>> } >>>> >>>> return ret; >>>> } >>>> int decrement() >>>> { >>>> int ret; >>>> >>>> if (parent) ret = parent.decrement(); >>>> else { >>>> synchronized(this) >>>> { >>>> ret = --refs; >>>> writefln("ThreadID=(",Thread.getThis().id,") decrement to >>>> Ref=(",refs,")"); >>>> } >>>> } >>>> >>>> return ret; >>>> } >>>> } >>>> >>>> class Resource >>>> { >>>> char[] name = "11 was a racehorse"; >>>> char[] toString() { return name; } >>>> } >>>> >>>> RefPtr pbob; >>>> >>>> static this() >>>> { >>>> pbob = new RefPtr(new Resource()); >>>> } >>>> >>>> static ~this() >>>> { >>>> delete pbob; >>>> } >>>> >>>> void main() >>>> { >>>> Thread[] threads; >>>> int i; >>>> >>>> writefln("ThreadID=(",Thread.getThis().id,") is the main thread"); >>>> >>>> for(i = 0; i < 10; i++) >>>> { >>>> threads ~= new Thread(&thread_function,null); >>>> threads[$-1].start(); >>>> } >>>> >>>> while(true) >>>> { >>>> i = 0; >>>> foreach(Thread t; threads) >>>> { >>>> if (t.getState() == Thread.TS.TERMINATED) i++; >>>> } >>>> if (i == 10) break; >>>> Sleep(100); >>>> } >>>> >>>> writefln("Main exiting"); >>>> } >>>> >>>> int thread_function(void* isnull) >>>> { >>>> auto RefPtr p = new RefPtr(pbob); >>>> Sleep(1000+rand()%1000); return 0; >>>> } >>>> >>>> Regan >>> >>> >> > |
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | On Sun, 26 Jun 2005 22:40:03 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: > "Regan Heath" <regan@netwin.co.nz> wrote in message > void main() > { > Resource Bob = new Resource(); > Resource Mary = new Resource(); > > auto AutoPtr p = new AutoPtr(Bob); > auto AutoPtr q; > > p = Mary; // What will happen here? ERROR: cannot implicitly convert expression (Mary) of type autoptr.Resource to autoptr.AutoPtr (note "autoptr" was the name of the source file in my case) > q = p; // And here? The reference 'q' will now equal the reference 'p'. > } > > In D, for classes, > > q = p > > means assignment of reference and not value Correct. > this is why classes do not work here. I disagree. They *work* fine. If you meant to create a new AutoPtr and transfer ownership you should have used the copy constructor, eg. q = new AutoPtr(p); What you did instead was make a 2nd reference to the same AutoPtr. This is part of D's design goals (as I read them on the web site) classes shall not have opAssign instead you shall use a copy constructor. I can however see/understand the desire to make "q = p" an error, in order to prevent incorrect usage (as above). Have you a suggestion as to how to do that? (one that does not involve adding opAssign to classes) The way forwards, as I see it, is to solve the question above. Asking for opAssign for classes will likely get us nowhere. Of course the other option is to use a struct, the thread you posted earlier: http://www.digitalmars.com/d/archives/7988.html tends to suggest to me that struct will not be modified to solve this issue. > Having opAssign implemented allows > to implement control of assigning values - > to catch moment when *variable* > is getting new value. Notice you say "value" above. You do not assign values to reference, you assign references to references, thus opAssign has no place in classes. (I believe we've already agreed to this point elsewhere). Regan |
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0r4ry023k2f5@nrage.netwin.co.nz... > On Sun, 26 Jun 2005 22:40:03 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: >> "Regan Heath" <regan@netwin.co.nz> wrote in message >> void main() >> { >> Resource Bob = new Resource(); >> Resource Mary = new Resource(); >> >> auto AutoPtr p = new AutoPtr(Bob); >> auto AutoPtr q; >> >> p = Mary; // What will happen here? > > ERROR: cannot implicitly convert expression (Mary) of type autoptr.Resource to autoptr.AutoPtr > > (note "autoptr" was the name of the source file in my case) > >> q = p; // And here? > > The reference 'q' will now equal the reference 'p'. > >> } >> >> In D, for classes, >> >> q = p >> >> means assignment of reference and not value > > Correct. > >> this is why classes do not work here. > > I disagree. They *work* fine. If you meant to create a new AutoPtr and transfer ownership you should have used the copy constructor, eg. > > q = new AutoPtr(p); Intention for q = p here is: 1) to free object q is holding. 2) to transfer ownership from p to q. If p and q will be deleted then only owner will delete owned object - not both of them. > > What you did instead was make a 2nd reference to the same AutoPtr. > > This is part of D's design goals (as I read them on the web site) classes shall not have opAssign instead you shall use a copy constructor. > > I can however see/understand the desire to make "q = p" an error, in order to prevent incorrect usage (as above). Have you a suggestion as to how to do that? (one that does not involve adding opAssign to classes) > > The way forwards, as I see it, is to solve the question above. Asking for opAssign for classes will likely get us nowhere. > > Of course the other option is to use a struct, the thread you posted > earlier: > http://www.digitalmars.com/d/archives/7988.html > > tends to suggest to me that struct will not be modified to solve this issue. > >> Having opAssign implemented allows >> to implement control of assigning values - >> to catch moment when *variable* >> is getting new value. > > Notice you say "value" above. You do not assign values to reference, you assign references to references, thus opAssign has no place in classes. (I believe we've already agreed to this point elsewhere). Yes. This is why I am using struct in my example. For classes '=' is copying references. For sructures it is copying values. The problem is that for structures '=' silently overrides values without any control from your side. |
June 27, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | On Mon, 27 Jun 2005 08:55:50 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: > "Regan Heath" <regan@netwin.co.nz> wrote in message > news:opss0r4ry023k2f5@nrage.netwin.co.nz.. >> On Sun, 26 Jun 2005 22:40:03 -0700, Andrew Fedoniouk >> <news@terrainformatica.com> wrote: >>> "Regan Heath" <regan@netwin.co.nz> wrote in message >>> void main() >>> { >>> Resource Bob = new Resource(); >>> Resource Mary = new Resource(); >>> >>> auto AutoPtr p = new AutoPtr(Bob); >>> auto AutoPtr q; >>> >>> p = Mary; // What will happen here? >> >> ERROR: cannot implicitly convert expression (Mary) of type >> autoptr.Resource to autoptr.AutoPtr >> >> (note "autoptr" was the name of the source file in my case) >> >>> q = p; // And here? >> >> The reference 'q' will now equal the reference 'p'. >> >>> } >>> >>> In D, for classes, >>> >>> q = p >>> >>> means assignment of reference and not value >> >> Correct. >> >>> this is why classes do not work here. >> >> I disagree. They *work* fine. If you meant to create a new AutoPtr and >> transfer ownership you should have used the copy constructor, eg. >> >> q = new AutoPtr(p); > > Intention for > q = p here is: > 1) to free object q is holding. > 2) to transfer ownership from p to q. > > If p and q will be deleted then only owner > will delete owned object - not both of them. I know what the intention is. My code does that, provided you do not write "q = p" but instead write: q = new AutoPtr(p); You could also write "p = null;" to signal the GC may collect (tho if it's auto it probably doesn't?) Stop thinking in C++ and start thinking in D. I repeat. D does not have opAssign, where you want opAssign use a copy constructor. The _only_ problem I can see with the code I posted is that you cannot prevent someone accidently writing "q = p" when they meant to write "q = new AutoPtr(p);". I'm not sure what the solution is, but it is the way forward with this concept IMO. >> Notice you say "value" above. You do not assign values to reference, you >> assign references to references, thus opAssign has no place in classes. (I >> believe we've already agreed to this point elsewhere). > > Yes. This is why I am using struct in my example. > For classes '=' is copying references. > For sructures it is copying values. > The problem is that for structures '=' > silently overrides values without any control > from your side. Structs also do not have ctor/dtor. Classes are the only option for AutoPtr IMO. Regan |
June 28, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss1p7dkp23k2f5@nrage.netwin.co.nz... > On Mon, 27 Jun 2005 08:55:50 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: >> "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0r4ry023k2f5@nrage.netwin.co.nz.. >>> On Sun, 26 Jun 2005 22:40:03 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: >>>> "Regan Heath" <regan@netwin.co.nz> wrote in message >>>> void main() >>>> { >>>> Resource Bob = new Resource(); >>>> Resource Mary = new Resource(); >>>> >>>> auto AutoPtr p = new AutoPtr(Bob); >>>> auto AutoPtr q; >>>> >>>> p = Mary; // What will happen here? >>> >>> ERROR: cannot implicitly convert expression (Mary) of type >>> autoptr.Resource to autoptr.AutoPtr >>> >>> (note "autoptr" was the name of the source file in my case) >>> >>>> q = p; // And here? >>> >>> The reference 'q' will now equal the reference 'p'. >>> >>>> } >>>> >>>> In D, for classes, >>>> >>>> q = p >>>> >>>> means assignment of reference and not value >>> >>> Correct. >>> >>>> this is why classes do not work here. >>> >>> I disagree. They *work* fine. If you meant to create a new AutoPtr and transfer ownership you should have used the copy constructor, eg. >>> >>> q = new AutoPtr(p); >> >> Intention for >> q = p here is: >> 1) to free object q is holding. >> 2) to transfer ownership from p to q. >> >> If p and q will be deleted then only owner >> will delete owned object - not both of them. > > I know what the intention is. My code does that, provided you do not write > "q = p" but instead write: > q = new AutoPtr(p); > > You could also write "p = null;" to signal the GC may collect (tho if it's auto it probably doesn't?) > > Stop thinking in C++ and start thinking in D. I repeat. D does not have opAssign, where you want opAssign use a copy constructor. If I have C++ why do I need degradation? And about yours q = new AutoPtr(p); It does not call destructor of q on assignment so what is the point of this AutoPtr(p). 'Auto' means that it manages pointer for me. > > The _only_ problem I can see with the code I posted is that you cannot prevent someone accidently writing "q = p" when they meant to write "q = new AutoPtr(p);". I'm not sure what the solution is, but it is the way forward with this concept IMO. > >>> Notice you say "value" above. You do not assign values to reference, you >>> assign references to references, thus opAssign has no place in classes. >>> (I >>> believe we've already agreed to this point elsewhere). >> >> Yes. This is why I am using struct in my example. >> For classes '=' is copying references. >> For sructures it is copying values. >> The problem is that for structures '=' >> silently overrides values without any control >> from your side. > > Structs also do not have ctor/dtor. > Classes are the only option for AutoPtr IMO. Without control of assignment and ctor/dtor it is impossible to implement the whole class of smart pointers and such thing as COW string. We agreed that opAssign in classes makes no sense. So only structs left. About real COW strings and motivation: http://www.wxwidgets.org/manuals/2.6.0/wx_wxstringoverview.html#wxstringrefcount It is impossible to reproduce in D such class and any other class based on pointer management paradigm. I mean to define string class which will behave as a string type and not just a set of C style functions dealing with struct slice { char* ptr; size_t length; }. Which is fine if we agree that D is a next letter after C and not after C++. Again, it depends on author motivation. As far as I can see there are three features that C++ has and D doesn't have: struct(opAssign/ctor/dtor) and const. If they will be there then D may be considered as a successor of C++. With all possible implications. IMHO. Andrew. |
June 28, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | On Mon, 27 Jun 2005 21:31:18 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote: > And about yours q = new AutoPtr(p); > It does not call destructor of q on assignment True, In D the destructor is only called when the object is collected, or you call "delete". > so what is the point of this AutoPtr(p). > 'Auto' means that it manages pointer for > me. Well, it seems to me that one solution would be for an 'auto' class to be deleted the moment it is no longer referenced. (rather than waiting till the end of the scope). Not sure if that is possible, but it makes sense. So, that just leaves the problem of preventing a normal reference assignment, got any ideas? >>>> Notice you say "value" above. You do not assign values to reference, you >>>> assign references to references, thus opAssign has no place in classes. >>>> (I >>>> believe we've already agreed to this point elsewhere). >>> >>> Yes. This is why I am using struct in my example. >>> For classes '=' is copying references. >>> For sructures it is copying values. >>> The problem is that for structures '=' >>> silently overrides values without any control >>> from your side. >> >> Structs also do not have ctor/dtor. >> Classes are the only option for AutoPtr IMO. > > Without control of assignment and ctor/dtor > it is impossible to implement the whole class of > smart pointers and such thing as COW string. Perhaps, but that does not mean we require opAssign. There may be another way. > We agreed that opAssign in classes makes no > sense. So only structs left. We could still use classes without using opAssign. This is where I have concentrated my efforts since reading the thread you found from back in 2002 where they decide on using 'auto'. > About real COW strings and motivation: > http://www.wxwidgets.org/manuals/2.6.0/wx_wxstringoverview.html#wxstringrefcount > > It is impossible to reproduce in D such class > and any other class based on pointer management > paradigm. It's currently impossible to reproduce classes that work in 'exactly' the same way. The D solution does not have to be exactly the same, provided it still achieve the goals. > I mean to define string class which > will behave as a string type and not just a set > of C style functions dealing with > struct slice { char* ptr; size_t length; }. What would this string class do that char[] cannot? If your only motivation is to provide a way of passing 'const' string data then I think enforcing readonly for "const" and "in" is much more productive and would also work for other types in D. > Again, it depends on author motivation. > > As far as I can see there are three features > that C++ has and D doesn't have: > struct(opAssign/ctor/dtor) and const. > > If they will be there then D may be considered > as a successor of C++. With all possible > implications. You have to remember that D is different to C++ and will therefore solve the same problems in different ways. I'm looking for a D solution. You seem to be asking for D to become C++. I agree D cannot solve these problems, yet, but the best way to fix that is to think of a way it could solve them *without* changing some of the fundamental design decisions that make D what it is. IMO. Regan |
Copyright © 1999-2021 by the D Language Foundation