www.digitalmars.com         C & C++   DMDScript  

D - Force-delete objects?

reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I think that it would be useful to be able to have a "force-delete"
storage class (or some such) for CLASS REFERENCES that would
automatically delete the class object when the reference went out of
scope.  That is, basically, it would be a class object that was NOT
garbage collected.

This would be useful for things that need to be cleaned up quickly.
People have suggested that open files should be cleaned up (to close the
file); I run across things in multithreaded programming that are
similar.  In C++, I do things like this:


class Lock;
class Locker
{
private:
    Lock *lockThis;

public:
    Locker(Lock *lockThis) { this->lockThis = lockThis;
lockThis->Lock(); };
    ~Locker() { lockThis->Unlock(); };
};



which allows me to write code like this:



{
    Locker lock(collectionObject->lock)
        ... // do stuff with collection Object
} // collectionObject is automatically unlocked when we go out of scope,

  // either by returning, continuing out of the block, or throwing an
exception



Obviously, in my style of code, delayed GC is NOT acceptable.  You could
force the programmer to call delete; as he leaves the block...but the
whole point of this object was to make it so the programmer DIDN'T have
to remember to unlock everything - it's a failsafe.

--
The Villagers are Online! villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Mar 13 2002
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Russ Lewis wrote:

 I think that it would be useful to be able to have a "force-delete"
 storage class (or some such) for CLASS REFERENCES that would
 automatically delete the class object when the reference went out of
 scope.  That is, basically, it would be a class object that was NOT
 garbage collected.

A note: This is where I (again) am really in favor of a reference counting GC. If you had reference counting, then you could make a syntax rule that you could not mix "force-delete" and normal references in assignments. Then, you could still have the object persist beyond the scope of the block, but it would IMMEDIATELY be deleted when the last reference went away. Yes, yes, I know loops are a problem. But David Bacon (of IBM Research) has a system that can auto-detect loops in a reference-counting system. If you use a non-concurrent collector (i.e. you stop all threads in the program while the collector runs), then the algorithm to detect loops is actually remarkably trivial. He has also posted another, more complex, test that will work with concurrent collectors. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Mar 13 2002
parent "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C8F883A.3C5903F0 deming-os.org...
 A note:

 This is where I (again) am really in favor of a reference counting GC.  If
 you had reference counting, then you could make a syntax rule that you

 not mix "force-delete" and normal references in assignments.  Then, you
 could still have the object persist beyond the scope of the block, but it
 would IMMEDIATELY be deleted when the last reference went away.

 Yes, yes, I know loops are a problem.  But David Bacon (of IBM Research)

 a system that can auto-detect loops in a reference-counting system.  If

 use a non-concurrent collector (i.e. you stop all threads in the program
 while the collector runs), then the algorithm to detect loops is actually
 remarkably trivial.  He has also posted another, more complex, test that
 will work with concurrent collectors.

Reference counting would be a big problem to implement in D due to D's support of interior pointers possibly being the only references to an object. Interior pointers are necessary to support array slicing and interfacing easilly to C.
Mar 13 2002
prev sibling next sibling parent reply "Immanuel Scholz" <digital-mars kutzsche.net> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> schrieb im Newsbeitrag
news:3C8F869F.F8A5B82A deming-os.org...
 I think that it would be useful to be able to have a "force-delete"
 storage class (or some such) for CLASS REFERENCES that would
 automatically delete the class object when the reference went out of
 scope.  That is, basically, it would be a class object that was NOT
 garbage collected.

I thought calling delete on an object immediatly destroy it and does not wait until gc catch up? If this is not so, I think it should be made like this, or are there reasons to not make delete act like this?
 This would be useful for things that need to be cleaned up quickly.
 People have suggested that open files should be cleaned up (to close the
 file); I run across things in multithreaded programming that are
 similar.  In C++, I do things like this:

as you mention the word "multithreaded", I remember a issue, where reference counting can heavily impact the performance on multithreaded enviroment. Are there the possibility to forbid reference counting on objectst? Maybe with a keyword?
 class Lock;
 class Locker
 {
 private:
     Lock *lockThis;

 public:
     Locker(Lock *lockThis) { this->lockThis = lockThis;
 lockThis->Lock(); };
     ~Locker() { lockThis->Unlock(); };
 };



 which allows me to write code like this:



 {
     Locker lock(collectionObject->lock)
         ... // do stuff with collection Object
 } // collectionObject is automatically unlocked when we go out of scope,

   // either by returning, continuing out of the block, or throwing an
 exception

I think "RAII" (resource allocation is initialisation) in D is written like this: { lock_something(); // allocation ... } finally { unlock(); // deallocation } I dislike this in some kind too, because it lacks some of the coolines of the c++ - way (as example, the destructor is never called, when an exception within the constructor is thrown), but I think I may get used to it...
 Obviously, in my style of code, delayed GC is NOT acceptable.  You could
 force the programmer to call delete; as he leaves the block...but the
 whole point of this object was to make it so the programmer DIDN'T have
 to remember to unlock everything - it's a failsafe.

You may directly call to the destructor in finally {}... But since you may call to unlock as well, there is no more use for a sentry-class like Locker Imi
Mar 13 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Immanuel Scholz wrote:

 I thought calling delete on an object immediatly destroy it and does not
 wait until gc catch up?

 If this is not so, I think it should be made like this, or are there reasons
 to not make delete act like this?

No, your understanding is correct. (Or, at least, it matches my understanding.) But if I force programmers to code a delete, then there's no need for my Locker class.
 as you mention the word "multithreaded", I remember a issue, where
 reference counting can heavily impact the performance on multithreaded
 enviroment.

 Are there the possibility to forbid reference counting on objectst?
 Maybe with a keyword?

Currently, D's GC is NOT a reference counter (see my other post on the subject).
 I think "RAII" (resource allocation is initialisation) in D is written like
 this:

 {
    lock_something();    // allocation

   ...
 }
 finally {
   unlock();        // deallocation
 }

Right. D can do this far more cleanly than C++ (we can consolidate all of the cleanup into the finally block), but it still forces programmers to remember it. I have had way too many forgotten locks in my own code to trust my own memory :) -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Mar 13 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Immanuel Scholz" <digital-mars kutzsche.net> wrote in message
news:a6o69m$1vif$1 digitaldaemon.com...

 I thought calling delete on an object immediatly destroy it and does not
 wait until gc catch up?

It does. What Russ wants is an object that's deleted _automatically_ when its scope ends.
Mar 13 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a6o7s4$206h$1 digitaldaemon.com...
 "Immanuel Scholz" <digital-mars kutzsche.net> wrote in message
 news:a6o69m$1vif$1 digitaldaemon.com...
 I thought calling delete on an object immediatly destroy it and does not
 wait until gc catch up?

when its scope ends.

What this does is add back in C++ semantics for auto-destruction of objects. Unfortunately, that drags along a great deal of complexity internal to the compiler, coupled with all kinds of obscure rules about when temporaries are destructed, interactions with various features like ?:, and lots of inevitable obscure implementation bugs. Adding this in will mean that D will no longer be a small, easy to understand and implement language. Far and away most uses of resource deallocation are memory releases, which is not necessary with garbage collection. The next most common use is releasing mutex locks, which is handled by the "synchronize" features of D. What's left should not be too onerous to use with explicit finally blocks.
Mar 16 2002
next sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
Destructor semantics are very handy.  Let's say I have a 3D Model object

class Model3D
{
private:
  IDirect3DVertexBuffer8* vb;
  IDirect3DTexture8* tex;
public:
  this() { /+ load model here +/ }
  ~this() { vb->Release(); tex->Release(); }
};

If your language gives you no guarantees that the destructor will ever get
called, you cannot make sure that your program leaves something in a
consistent state.  You can't use try...finally in the above situation.

This kind of thing is more common than you think.  It's not just mutex's and
memory we have to manage.

Sean


"Walter" <walter digitalmars.com> wrote in message
news:a707f0$1qif$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:a6o7s4$206h$1 digitaldaemon.com...
 "Immanuel Scholz" <digital-mars kutzsche.net> wrote in message
 news:a6o69m$1vif$1 digitaldaemon.com...
 I thought calling delete on an object immediatly destroy it and does



 wait until gc catch up?

when its scope ends.

What this does is add back in C++ semantics for auto-destruction of

 Unfortunately, that drags along a great deal of complexity internal to the
 compiler, coupled with all kinds of obscure rules about when temporaries

 destructed, interactions with various features like ?:, and lots of
 inevitable obscure implementation bugs.

 Adding this in will mean that D will no longer be a small, easy to
 understand and implement language.

 Far and away most uses of resource deallocation are memory releases, which
 is not necessary with garbage collection. The next most common use is
 releasing mutex locks, which is handled by the "synchronize" features of

 What's left should not be too onerous to use with explicit finally blocks.

Mar 16 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:a70eoj$1ujd$1 digitaldaemon.com...
 Destructor semantics are very handy.  Let's say I have a 3D Model object

 class Model3D
 {
 private:
   IDirect3DVertexBuffer8* vb;
   IDirect3DTexture8* tex;
 public:
   this() { /+ load model here +/ }
   ~this() { vb->Release(); tex->Release(); }
 };

 If your language gives you no guarantees that the destructor will ever get
 called, you cannot make sure that your program leaves something in a
 consistent state.  You can't use try...finally in the above situation.

 This kind of thing is more common than you think.  It's not just mutex's

 memory we have to manage.

I understand the problem. What you can do is construct a list of such critical objects, and run the release on them in a finally block off of the program entry point.
Mar 16 2002
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
Does that not sound like a gigantic kludge to you?  I don't want to be
polluting my main() function with stuff that conceptually should be done in
a finalize method of an object.

Sure we can use delete obj; and manually call the GC when we need to... I
would just like some guarantees from the language that the destructor will
get called for every object that is collected, and that every object will be
collected as the program shuts down.  If the compiler could automatically
"collect" objects that simply go out of scope when it knows that no pointers
to the object have been stored anywhere (the code never takes the address of
the object for instance, or never stores it or sends it anywhere), that
would be real nice too.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:a71609$2dcd$1 digitaldaemon.com...
 "Sean L. Palmer" <spalmer iname.com> wrote in message
 news:a70eoj$1ujd$1 digitaldaemon.com...
 Destructor semantics are very handy.  Let's say I have a 3D Model object

 class Model3D
 {
 private:
   IDirect3DVertexBuffer8* vb;
   IDirect3DTexture8* tex;
 public:
   this() { /+ load model here +/ }
   ~this() { vb->Release(); tex->Release(); }
 };

 If your language gives you no guarantees that the destructor will ever


 called, you cannot make sure that your program leaves something in a
 consistent state.  You can't use try...finally in the above situation.

 This kind of thing is more common than you think.  It's not just mutex's

 memory we have to manage.

I understand the problem. What you can do is construct a list of such critical objects, and run the release on them in a finally block off of

 program entry point.

Mar 17 2002
parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:a732ft$gum$1 digitaldaemon.com...
 Does that not sound like a gigantic kludge to you?  I don't want to be
 polluting my main() function with stuff that conceptually should be done

 a finalize method of an object.

It doesn't have to be main(), just places in your program that suggest themselves as a convenient place to put them.
 Sure we can use delete obj; and manually call the GC when we need to... I
 would just like some guarantees from the language that the destructor will
 get called for every object that is collected, and that every object will

 collected as the program shuts down.

main() does automatically run a collection cycle upon completion. However, it is still possible that there's a dangling reference in the stack and static data. It's possible the gc could be changed to ignore them for the final collection.
  If the compiler could automatically
 "collect" objects that simply go out of scope when it knows that no

 to the object have been stored anywhere (the code never takes the address

 the object for instance, or never stores it or sends it anywhere), that
 would be real nice too.

The problem is it isn't simple. There's a lot of implementation required to get it right.
Mar 19 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a707f0$1qif$1 digitaldaemon.com...

 What this does is add back in C++ semantics for auto-destruction of

 Unfortunately, that drags along a great deal of complexity internal to the
 compiler, coupled with all kinds of obscure rules about when temporaries

 destructed, interactions with various features like ?:, and lots of
 inevitable obscure implementation bugs.

 Adding this in will mean that D will no longer be a small, easy to
 understand and implement language.

No, no, that wasn't the point. The idea was to add some kind of attribute ("auto"?) for locals that forces them to be deleted immediately when their scope ends. It has nothing to do with temporaries or C++-style stack objects: void foo() { auto File file = new File; ... // file is automatically deleted at the end of function }
Mar 17 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a71isi$2kvd$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:a707f0$1qif$1 digitaldaemon.com...
 What this does is add back in C++ semantics for auto-destruction of

 Unfortunately, that drags along a great deal of complexity internal to


 compiler, coupled with all kinds of obscure rules about when temporaries

 destructed, interactions with various features like ?:, and lots of
 inevitable obscure implementation bugs.
 Adding this in will mean that D will no longer be a small, easy to
 understand and implement language.

("auto"?) for locals that forces them to be deleted immediately when their scope

 It has nothing to do with temporaries or C++-style stack objects:
     void foo()
     {
         auto File file = new File;
         ...
         // file is automatically deleted at the end of function
     }

I think it is analogous to the way C++ calls destructors on stack objects when they go out of scope. The problem isn't that they are on the stack, but the complicated rules for doing the destructors.
Mar 17 2002
next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a72klf$6ft$1 digitaldaemon.com...

 I think it is analogous to the way C++ calls destructors on stack objects
 when they go out of scope. The problem isn't that they are on the stack,

 the complicated rules for doing the destructors.

What's wrong with the rules? Just destruct the objects in reversed order: File a, b; ... // b destroyed // a destroyed I don't see any complexity here... where am I wrong?
Mar 17 2002
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:

 I think it is analogous to the way C++ calls destructors on stack objects
 when they go out of scope. The problem isn't that they are on the stack, but
 the complicated rules for doing the destructors.

Can't we just say that for these types of objects the compiler implicitly generates a finally {} block which deletes them? It does add some complexity, but not much. Compilers that don't support that feature (yet) just throw compile-time errors on the type modifier :) -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Mar 18 2002
next sibling parent Russell Borogove <kaleja estarcion.com> writes:
Russ Lewis wrote:
 Walter wrote:
 
 
I think it is analogous to the way C++ calls destructors on stack objects
when they go out of scope. The problem isn't that they are on the stack, but
the complicated rules for doing the destructors.

Can't we just say that for these types of objects the compiler implicitly generates a finally {} block which deletes them? It does add some complexity, but not much. Compilers that don't support that feature (yet) just throw compile-time errors on the type modifier :)

I'll just throw in my $0.02 here -- one of the few places where C++ really becomes elegant is when you want to put a resource-grabbing object, or a debugging object as an auto variable in a function, and have it naturally cleaned up on function exit. Reviving C's vestigial "auto" declaration to say "please destroy at the end of this function/block" seems like a reasonable way to do this. -RB
Mar 18 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C963321.EFBC360F deming-os.org...
 Walter wrote:
 I think it is analogous to the way C++ calls destructors on stack


 when they go out of scope. The problem isn't that they are on the stack,


 the complicated rules for doing the destructors.

generates a finally {} block which deletes them? It does add some

 but not much.  Compilers that don't support that feature (yet) just throw
 compile-time errors on the type modifier :)

That's just what C++ does. Things get complicated, though, for destruction of temporaries that may or may not have been created, for ?: expressions that might have been short circuited, construction objects as function parameters, and then an exception happens before the function gets called, etc.
Mar 23 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a7k4io$mbt$1 digitaldaemon.com...

 Things get complicated, though, for destruction of temporaries that may or
 may not have been created, for ?: expressions that might have been short
 circuited, construction objects as function parameters, and then an
 exception happens before the function gets called, etc.

There won't be any temproraries there, since we work with references rather than objects. This is an important difference from C++ - here, the only thing the compiler should do is to guarantee that any auto reference gets deleted at the end of the scope. In fact, it just means implicitly inserting delete statements at the end of the block, after every break, and in final section of each try block. Another tricky thing is that the block must be wrapped an invisible try..finally block: void foo() { while (true) { /* try { */ auto File file = new File; ... if (file.eof()) { /* delete file; */ break; } ... file.read(n); // could raise an exception ... try file.write(n); finally { n = 0; /* delete file; */ } ... /* } finally { delete file; } */ } } Of course, there's also goto, but in this case you could just state that "goto doesn't delete auto references" - so if somebody is smart enough to goto out of block (instead of, say, break'ing), he should take care of deleting all autos himself. Are these rules complex? I wouldn't say so...
Mar 24 2002
parent reply Patrick Down <pat codemoon.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in
news:a7keme$1aig$1 digitaldaemon.com: 

 "Walter" <walter digitalmars.com> wrote in message
 news:a7k4io$mbt$1 digitaldaemon.com...
 
 Things get complicated, though, for destruction of temporaries that
 may or may not have been created, for ?: expressions that might have
 been short circuited, construction objects as function parameters,
 and then an exception happens before the function gets called, etc.

There won't be any temproraries there, since we work with references rather than objects. This is an important difference from C++ - here, the only thing the compiler should do is to guarantee that any auto reference gets deleted at the end of the scope. In fact, it just means implicitly inserting delete statements at the end of the block, after every break, and in final section of each try block. Another tricky thing is that the block must be wrapped an invisible try..finally block:

What about this... void bar(out Obj a) { a = new Obj; } void foo() { auto Obj b = new Obj; bar(b); }
Mar 24 2002
parent "Pavel Minayev" <evilone omen.ru> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns91DB6CC9E77BApatcodemooncom 63.105.9.61...

 What about this...

 void bar(out Obj a)
 {
   a = new Obj;
 }

 void foo()
 {
   auto Obj b = new Obj;

   bar(b);
 }

The object created first is collected by the GC, since there are no references left to it. The second one (constructed in bar()) gets deleted at the end of function. This leads to a decision that all auto objects shouldn't be changed once assigned, as if it was declared const in C++.
Mar 24 2002
prev sibling parent reply "Patrick Down" <pdown austin.rr.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a71isi$2kvd$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:a707f0$1qif$1 digitaldaemon.com...

 Adding this in will mean that D will no longer be a small, easy to
 understand and implement language.

No, no, that wasn't the point. The idea was to add some kind of attribute ("auto"?) for locals that forces them to be deleted immediately when their scope

 It has nothing to do with temporaries or C++-style stack objects:

     void foo()
     {
         auto File file = new File;
         ...
         // file is automatically deleted at the end of function
     }

 Unfortunately, that drags along a great deal of complexity internal
 to the compiler, coupled with all kinds of obscure rules about when
 temporaries

 destructed, interactions with various features like ?:, and lots of
 inevitable obscure implementation bugs.

 Adding this in will mean that D will no longer be a small, easy to
 understand and implement language.

No, no, that wasn't the point. The idea was to add some kind of attribute ("auto"?) for locals that forces them to be deleted immediately when their scope ends. It has nothing to do with temporaries or C++-style stack objects: void foo() { auto File file = new File; ... // file is automatically deleted at the end of function }

I think the problem that Walter has with this is that determining when an object's scope ends is a hard. Just consider looping or try statement scopes with break, continue, return, or goto statements. But I wonder since we are only talking about heap based objects if there is not a easier way to handle this. Consider the following code. int foo() { int doneOnce = 0; lable1: auto File file = new File; for(int i=0; i < 10; ++i) { auto SomeObj a = new SomeObj; if(a.SomeFunc()) continue; auto SomeObj b = new SomeObj; if(b.SomeFunc()) return 1; } if(doneOnce) { doneOnce = 1; goto lable1; } return 2; } Could this be made to work with some very simple rules? 1. Determine all your auto varaibles an initialize them to null at the beginning of the function. 2. Make a cleanup section that deletes all the autos at the end of the function. 3. Every assignment to an auto variable is preceeded by a delete of that object. So the compiler would translate translate the above function into something like this? Assume delete does a null test. int foo() { int rtn; File file = null; SomeObj a = null; SomeObj b = null; int doneOnce = 0; lable1: delete file; file = new File; for(int i=0; i < 10; ++i) { delete a; a = new SomeObj; if(a.SomeFunc()) continue; delete b; b = new SomeObj; if(b.SomeFunc()) { rtn = 1; goto cleanup; } } if(doneOnce) { doneOnce = 1; goto lable1; } rtn = 2; cleanup: delete file; delete a; delete b; return rtn; } Now function calls and return statements need a little consideration. out paramneters are like assignments so rule 3 applies. Since you really can't tell with an inout don't allow it. Don't allow auto variables to be returned for obvious reasons. void FuncIn(in SomeObj a) { } void FuncOut(out SomeObj a) { } void FuncInOut(inout SomeObj a) { } SomeObj foo() { auto SomeObj a = new SomeObj; // This is ok just hope FuncIn doesn't do somthing // silly like store the object or delete it FuncIn(a); // This is ok FuncOut(a); // but a must be deleted before the function call // This is an error can't use auto variables as inout // parameters FuncInOut(a); // This is an error can't return auto variables return a; }
Mar 17 2002
parent reply "Roberto Mariottini" <rmariottini lycosmail.com> writes:
"Patrick Down" <pdown austin.rr.com> ha scritto nel messaggio
news:a72u86$dkt$1 digitaldaemon.com...
[...]
 Could this be made to work with some very simple rules?
 1. Determine all your auto varaibles an initialize them to
 null at the beginning of the function.
 2. Make a cleanup section that deletes all the autos at
 the end of the function.
 3. Every assignment to an auto variable is preceeded by
 a delete of that object.

It seems to me a good starting point. But how will you deal with exceptions?
 So the compiler would translate translate the above function
 into something like this? Assume delete does a null test.

 Now function calls and return statements need a little
 consideration.  out paramneters are like assignments
 so rule 3 applies. Since you really can't tell with an
 inout don't allow it.  Don't allow auto variables to
 be returned for obvious reasons.


 void FuncIn(in SomeObj a)
 {
 }

 void FuncOut(out SomeObj a)
 {
 }

 void FuncInOut(inout SomeObj a)
 {
 }

 SomeObj foo()
 {
   auto SomeObj a = new SomeObj;

   // This is ok just hope FuncIn doesn't do somthing
   // silly like store the object or delete it
   FuncIn(a);

I can live with this security limitation.
   // This is ok
   FuncOut(a);
   // but a must be deleted before the function call

   // This is an error can't use auto variables as inout
   // parameters
   FuncInOut(a);

Why?
   // This is an error can't return auto variables
   return a;
 }

And what if one of theese functions do a "delete" on the auto object? And what about assignments? I mean { auto Object1 a; Object1 b; b = a; Func(b); // storing the pointer? } // now going out of scope... Ciao
Mar 18 2002
parent reply Patrick Down <pat codemoon.com> writes:
=====================================================
"Roberto Mariottini" <rmariottini lycosmail.com> wrote in
news:a74o32$1ne7$1 digitaldaemon.com: 

 
 "Patrick Down" <pdown austin.rr.com> ha scritto nel messaggio
 news:a72u86$dkt$1 digitaldaemon.com...
 [...]
 Could this be made to work with some very simple rules?
 1. Determine all your auto varaibles an initialize them to
 null at the beginning of the function.
 2. Make a cleanup section that deletes all the autos at
 the end of the function.
 3. Every assignment to an auto variable is preceeded by
 a delete of that object.

It seems to me a good starting point. But how will you deal with exceptions?

Yes, this is a problem. Right now I don't think D needs very complicated code for stack unwinding on an exception. But the auto stuff would require a visit to every frame's cleanup section.
   // This is an error can't use auto variables as inout
   // parameters
   FuncInOut(a);

Why?

inout implies that the parameter could be modified. You could do something like this. SomeObj temp = a; FuncInOut(a); if(temp != a) delete temp;
 
 {
 auto Object1 a;
 Object1 b;
 b = a;
 Func(b); // storing the pointer?
 } // now going out of scope...

I don't have a really good solution for this one and there are many of other ways to get in trouble. For example... Object a = new Object; while(...) { auto Object b = a; // a is deleted after one time // thru the loop } The compiler substitution should be changed to something like this... if(a != b) delete a; a = b; Also you can't do this... auto Object a; auto Object b; a = b; Which means this is a problem... auto Object a; auto Object b; Object c; c = a; b = c; There are many of ways this could go wrong. I guess it depends on if a auto delete pointer is worth all the potential programmer error.
Mar 18 2002
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Patrick Down wrote:

 Yes, this is a problem.  Right now I don't think D needs very
 complicated code for stack unwinding on an exception.  But the
 auto stuff would require a visit to every frame's cleanup section.

Force-delete doesn't impact the complexity of cleanup code. In any block, you can manually add a finally {} block, which must run when the block exits or an exception is thrown. That's a "hand-made" force-delete. This whole thing is really an issue of whether or not the compiler should include syntax sugar to automatically add one of these cleanup blocks. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Mar 18 2002
prev sibling next sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
I like this idea, but don't think you'd have to specify the storage class
explicitly for it to work.  I bet the compiler could figure out whether any
references to an object created in a given scope could possibly survive past
the end of the block.  If it did, it'd allocate the object from the heap and
later GC it.  If no references could possibly survive, it could create the
object on the stack and automatically clean it as it goes out of scope.  A
reference to an object can "survive" a block by having a pointer to it or
one of its members passed as a parameter to a function call or directly
stored into a nonlocal variable.

Yes.  I definitely want this.

Sean

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C8F869F.F8A5B82A deming-os.org...
 I think that it would be useful to be able to have a "force-delete"
 storage class (or some such) for CLASS REFERENCES that would
 automatically delete the class object when the reference went out of
 scope.  That is, basically, it would be a class object that was NOT
 garbage collected.

 This would be useful for things that need to be cleaned up quickly.
 People have suggested that open files should be cleaned up (to close the
 file); I run across things in multithreaded programming that are
 similar.  In C++, I do things like this:


 class Lock;
 class Locker
 {
 private:
     Lock *lockThis;

 public:
     Locker(Lock *lockThis) { this->lockThis = lockThis;
 lockThis->Lock(); };
     ~Locker() { lockThis->Unlock(); };
 };



 which allows me to write code like this:



 {
     Locker lock(collectionObject->lock)
         ... // do stuff with collection Object
 } // collectionObject is automatically unlocked when we go out of scope,

   // either by returning, continuing out of the block, or throwing an
 exception



 Obviously, in my style of code, delayed GC is NOT acceptable.  You could
 force the programmer to call delete; as he leaves the block...but the
 whole point of this object was to make it so the programmer DIDN'T have
 to remember to unlock everything - it's a failsafe.

Mar 13 2002
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
"Sean L. Palmer" wrote:

 I like this idea, but don't think you'd have to specify the storage class
 explicitly for it to work.  I bet the compiler could figure out whether any
 references to an object created in a given scope could possibly survive past
 the end of the block.  If it did, it'd allocate the object from the heap and
 later GC it.  If no references could possibly survive, it could create the
 object on the stack and automatically clean it as it goes out of scope.  A
 reference to an object can "survive" a block by having a pointer to it or
 one of its members passed as a parameter to a function call or directly
 stored into a nonlocal variable.

 Yes.  I definitely want this.

In general, this would work for most applications, but I also have code that passes pointers to Lockers (references in the D mindset) to other functions. Those functions guarantee not to change the lock in the long term, but they do muck with it during their course. So compiler auto-detection is unlikely to work, unless the compiler has VERY aggressive optimization :/ -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Mar 13 2002
prev sibling next sibling parent "Jim Starkey" <jas netfrastructure.com> writes:
Russ Lewis wrote in message <3C8F869F.F8A5B82A deming-os.org>...
I think that it would be useful to be able to have a "force-delete"
storage class (or some such) for CLASS REFERENCES that would
automatically delete the class object when the reference went out of
scope.  That is, basically, it would be a class object that was NOT
garbage collected.

This would be useful for things that need to be cleaned up quickly.
People have suggested that open files should be cleaned up (to close the
file); I run across things in multithreaded programming that are
similar.  In C++, I do things like this: [snip]

I think it's time to unravel the need for a feature and way it was implemented in C++. I think what you want is an encapsulated mechanism that a) has an implicit or explicit context, b) has a entrypoint called on entry to a block, and c) an entreepoint guaranteed to be called on block exit. Note that the semantics of "synchronized" in D and Java have exactly those characteristics, but are builtin rather than programmable. Perhaps the answer is replacement of "synchronized" with an extensible mechanism to allow other and/or additional resource managers to share the semantics. Among other things, this would allow the odd crank to synchronize with two state locks rather than mutexes. Sure would be a nice feature... Among the problems to be solved is argument passing. Although the two forms of "synchronized" take an implicit or explicit argument, the actually monitor is invisible, buried in the implementation. It's fairly clear that any generalization would require a little more infrastructure to be useful.
Mar 22 2002
prev sibling parent DrWhat? <blackmarlin nospam.asean-mail.com> writes:
I suppose the question is "when should an object be deleted?"
this could be ...
(1) when the procedure / method exits (object goes out of scope) := in 
which case the compiler could simply add an invisable finally block
(2) when a throw occurs := in which case the compiler schedules a garbage 
collection on all objects in the stack (after all throws are exceptions and 
need not be fast).
(3) when the task / job exits := another garbage collection cycle (think 
this is already specified in D standard)
(4) when an object is overwritten := in the same cases this is trivial - 
just add "delete <obj>;" before reassignment. - however in a called class 
this is difficult [void foo( inout a ) { a = new Obj; }] either we check a 
flag in the object to see if it should be deleted (yuck - slow (has to be 
done on every new)) or wait until the procedure returns,  check if old 
object == returned object and if not delete old object.

Finally we have a problem if the object is referenced outside the 
procedure,  ie. delete must occur on the object even after the force delete 
command in the procedure has been forgotten.

I suggest that if forced deletion is to be included that it works as 
follows.
(1) the object is created specifying forced deletion required
(2) the object continues to exist until the procedure it was defined in 
exits,  then the object is deleted - reguardless of any external references
- creating such references could be considered an error,  though catching 
this at compile time could be tricky.
(3) the object is deleted if a throw is thrown past the procedure - this 
could be in a finally block or by running a GC cycle (latter option assumes 
no external references).
(4) the object is deleted if it is reassigned within the procedure
(5) the object is deleted if it is reassigned within a called procedure - 
but only after that procedure returns

The only other way I can see to achieve this is to use reference counting - 
though I believe this has been (and should be) ruled out.

C 2002/3/25
Mar 25 2002