June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | >> or >> void foo(char[] x){ x[1] = 'd'; } > > error 'in' parameters are readonly. > >> char[] p = f.get(); >> foo(p); OK. And I assume the readonly flag gets cleared if passed to an inout or out parameter? >> I think it would need to be carried around as a different type. > > Most cases can be handled at compile time without that. Is there a compile-time mechanism to check for this flag? As a code maintainer how can one tell if the flag is set or not? > The case above that cannot, why does it need to be a seperate type? I dont think it does, it's the same type with a readonly bit set to true (rather than false). This bit can be checked at runtime, and disabled by -release. I'm not sure how a compile-time bit can be checked at run-time. Where is the bit stored during runtime? > One goal IMO is to remove the need for us to tell the compiler it's readonly when it can clearly tell all by itself (in many cases) and verify for itself in others (runtime readonly flag). I like the idea that the compiler (and/or dlint) can warn (or possibly error if the situation is completely unambiguous) when it thinks a readonly value is being modified. I've suggested before that dlint include flow analysis to warn about possible COW violations and other stuff like that. Runtime checks should be avoided, though. > So we don't need to use 'const' or 'readonly' everywhere eg. > > readonly char[] p = f.get(); > char[] s; > p = s; //error incompatible types > > >>> Further, 'in' parameters also require readonly enforcement, eg. >>> >>> Foo f = new Foo(); >>> mutate(f.get()); >>> >>> void mutate(char[] p) { >>> p[0] = 'd'; //error p is in/readonly >>> } >>> >>> if a function wants to modify a parameter, that parameter should be >>> 'out' >>> or 'inout', meaning, if we re-write mutate: >>> >>> Foo f = new Foo(); >>> mutate(f.get()); //error, f.get() is readonly >>> >>> void mutate(inout char[] p) { >>> p[0] = 'd'; //no error >>> } >> >> I could see explicit 'in' array parameters are readonly. I don't think >> that would cause much pain. It wouldn't have any effect on non-array >> parameters. >> Making implicit 'in' array parameters readonly would be too much IMHO. > > It has to be implicit to work. It's not bug tolerant otherwise. This is slightly OT but the phrase "bug tolerant" to me means the language tolerates bugs - which sounds like "bugs are ok". Of course I know this isn't what you mean by "bug tolerant" (I hope) but it's what I think of every time I read it. >> It is non-trivial making an 'in' parameter into 'inout' since 'inout' requires an lvalue while 'in' simply requires an rvalue. > > I can see how this would be painful for value types, but why is it painful for references (can I have an example?). With void foo(inout char[] x) { ... } char[] bar() { ... } it is not legal to write foo(bar()); Instead one has to write char[] temp = bar(); foo(temp); The output of bar is not an lvalue. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ben Hinkle | >> One goal IMO is to remove the need for us to tell the compiler it's readonly when it can clearly tell all by itself (in many cases) and verify for itself in others (runtime readonly flag). > > I like the idea that the compiler (and/or dlint) can warn (or possibly error if the situation is completely unambiguous) when it thinks a readonly value is being modified. I've suggested before that dlint include flow analysis to warn about possible COW violations and other stuff like that. Runtime checks should be avoided, though. For example I just hacked up a change to dlint with the following code in AssignExp::semantic() IdentifierExp* idexp = dynamic_cast<IdentifierExp*>(ae->e1); if (idexp != NULL) { Dsymbol* dsym = sc->search(idexp->ident,NULL); if (dsym != NULL) { Declaration* d = dsym->isDeclaration(); if (d != NULL && d->isParameter() && d->isIn()) error("Possible COW violation assigning through input parameter"); } } that will error (I will make that a warning next) if you perform indexed assignment on an input parameter. It's very basic and doesn't catch anything sneaky at all like void foo(char[] s) { char[] y = s; y[1] = 'd'; } but it will catch void foo(char[] s) { s[1] = 'd'; } |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ben Hinkle | Ben Hinkle wrote:
>>>One goal IMO is to remove the need for us to tell the compiler it's readonly when it can clearly tell all by itself (in many cases) and verify for itself in others (runtime readonly flag).
>>
>>I like the idea that the compiler (and/or dlint) can warn (or possibly error if the situation is completely unambiguous) when it thinks a readonly value is being modified. I've suggested before that dlint include flow analysis to warn about possible COW violations and other stuff like that. Runtime checks should be avoided, though.
>
>
> For example I just hacked up a change to dlint with the following code in AssignExp::semantic()
> IdentifierExp* idexp = dynamic_cast<IdentifierExp*>(ae->e1);
> if (idexp != NULL) {
> Dsymbol* dsym = sc->search(idexp->ident,NULL);
> if (dsym != NULL) {
> Declaration* d = dsym->isDeclaration();
> if (d != NULL && d->isParameter() && d->isIn())
> error("Possible COW violation assigning through input parameter");
> }
> }
> that will error (I will make that a warning next) if you perform indexed assignment on an input parameter. It's very basic and doesn't catch anything sneaky at all like
> void foo(char[] s) {
> char[] y = s;
> y[1] = 'd';
> }
> but it will catch
> void foo(char[] s) {
> s[1] = 'd';
> }
>
>
But how do you _know_ that is wrong? I don't see how the compiler/dlint can tell the difference between a correct case of changing the input array, and a bug. Which means that for the times that you actually want to index into an array, you get dlint grumbling to you for no reason.
Brad
|
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Beveridge | "Brad Beveridge" <brad@somewhere.net> wrote in message news:da1elm$1lp7$1@digitaldaemon.com... > Ben Hinkle wrote: >>>>One goal IMO is to remove the need for us to tell the compiler it's readonly when it can clearly tell all by itself (in many cases) and verify for itself in others (runtime readonly flag). >>> >>>I like the idea that the compiler (and/or dlint) can warn (or possibly error if the situation is completely unambiguous) when it thinks a readonly value is being modified. I've suggested before that dlint include flow analysis to warn about possible COW violations and other stuff like that. Runtime checks should be avoided, though. >> >> >> For example I just hacked up a change to dlint with the following code in >> AssignExp::semantic() >> IdentifierExp* idexp = dynamic_cast<IdentifierExp*>(ae->e1); >> if (idexp != NULL) { >> Dsymbol* dsym = sc->search(idexp->ident,NULL); >> if (dsym != NULL) { >> Declaration* d = dsym->isDeclaration(); >> if (d != NULL && d->isParameter() && d->isIn()) >> error("Possible COW violation assigning through input >> parameter"); >> } >> } >> that will error (I will make that a warning next) if you perform indexed >> assignment on an input parameter. It's very basic and doesn't catch >> anything sneaky at all like >> void foo(char[] s) { >> char[] y = s; >> y[1] = 'd'; >> } >> but it will catch >> void foo(char[] s) { >> s[1] = 'd'; >> } >> >> > But how do you _know_ that is wrong? I don't see how the compiler/dlint can tell the difference between a correct case of changing the input array, and a bug. Which means that for the times that you actually want to index into an array, you get dlint grumbling to you for no reason. Hence it should be a warning. Also note only indexed assignment through an 'in' parameter will warn. Arbitrary indexing is just fine and inout indexed assignment is fine so it won't grumble for most of the cases. Finally, in dlint there is a mechanism to tell dlint to squelch any warning on that line by putting (dlint) in a comment on the line. So something like void foo(char[] s) { s[1] = 'd'; //(dlint) } will shut up dlint if you know the assignment is fine. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Medlock | On Thu, 30 Jun 2005 09:29:19 -0400, David Medlock <noone@nowhere.com> wrote: > Andrew Fedoniouk wrote: >> "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss5jwkvj23k2f5@nrage.netwin.co.nz... >> >>> On Wed, 29 Jun 2005 09:41:01 -0400, David Medlock <noone@nowhere.com> wrote: >>> >>>> Regan Heath wrote: >>>> >>>> >>>>> Regan >>>> >>>> I thought the stack based types were to be useful for speed gains, not managing memory. >>> >>> Correct. We need/want the reference counter or auto pointer to be as fast as possible because they're typically instantiated and destroyed with abandon. Andrew posted a good example of this, where his graphics library needs to obtain ownership of a graphics context on every call to the 'paint' method, it needs to get it, use it and release it, this function is called very often. >>> > So why not simply grab the reference once per frame(as in frames per second) and draw all the needed objects? Dont pass the context to the objects, pass the objects to the context owner. > > I've written code of this type (managing texture memory in OpenGL for one) and its much easier managed on a per-frame basis than per object. > > (Sorry if I am not understanding this correctly, couldnt find the example you mention) I'll let Andrew pick this up if he wants. >>>> Why do you need reference counting? >>> >>> I don't, yet. Andrew does, now. >> Reference counting is one of most famous design patterns. >> RC as a memory management has its own limitations though ( e.g. cyclic refs ). >> Its power in those areas where GC is weak. And vice versa. >> So be able to use both will benefit the whole system twice. >> Good example of RC is in implementations of COW strings and shared_ptr >> in C++. >> > Those are required in C++ because there is no garbage collector. In C++ Copy on write is a programming discipline/design. Its still up to the programmer though, const just helps to keep the programmers honest. Yes, "const" helps make it bug tolerant. > With (non-transactional)concurrent programming, there is no way to use reference counting without locks at each assignment/deref. > > Consider: > A resource we call A has 2 references to it, owned by a variable in both Thread T and U. > > Thread T calls deref, but is blocked before it can check the reference count of A. > > Thread U calls deref as well, sees the reference count is zero, and deallocates the reference. > > Thread T awakens, sees the reference count is zero, calls destructor....OOPS. Yes, the process needs to be sync'ed/locked. > Not to mention modern architectures are very cache-sensitive. When you call destructors throughout your code, you 'thrash the cache' jumping around in memory a lot more than a gc calling the same destructors several times in sequence. > > I reject the assertion that managing references at each object is superior to an external collector(of any type, not simply memory). All those AddRef/Decref/Ref checks are not free. Oh I agree. But I'm not suggesting you'd want to use this for every object. Just certain objects. Being able to reply on the GC is great for the bulk of tasks, however certain objects, those that require explicit deterministic destruction are the ones you'd want to reference count. > Managed resources should be managed by the code, not the runtime. I'm sure some will disagree with some(or all) of this, but I have yet to see evidence to the contrary. Reference counting, like GC, is not a panacea, it's not the perfect solution to all memory management problems. What it is, is a useful tool for certain occasions, a tool which can be bug tolerant and easy to use (if only it were possible to write an implementation which was these things in D). Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Beveridge | On Thu, 30 Jun 2005 08:32:19 -0700, Brad Beveridge <brad@somewhere.net> wrote: > Regan Heath wrote: >> On Thu, 30 Jun 2005 07:58:33 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote: >> >>> Hmm. What about >>> char[] p = f.get(); >>> char[] q = p; >>> q[1] = 'd'; //is 'q' readonly? >> Yes. (as I said) The compile time readonly flag is passed on during assignment. > IMHO, the answer here should be "no". I would (I think) prefer that the line char[] p = f.get(); > > flags an error if f.get() happens to return an immutable char[]. Ie, you cannot explicitly assign from an immutable char[] to a char[]. Ahh, but that is half my point, they are not distinct types, nor do they need to be. > That leaves the problem solvable at compile time. The correct line would be It is still solvable at compile time. Every char[] has a compile time flag called readonly, which is flagged when 'in' or 'readonly' is used, and passed on every assignment/slice, and can be checked in cases like the one above. > immutable char[] p = f.get(); I want to avoid this at all costs, otherwise we get: char[] s; ..s is a temp var used up here.. immutable char[] p = f.get(); s = p[0..2]; //error, p is immuable in other words, we cannot reuse the variable and we get all these really annoying errors which then lead us to cast the immutable away (as is done with C++). > Now, for the sake of simplicity I do not think that the compiler should do run-time checking, nor should it prevent you from casting immutability away. IMHO all we want/need is a way to prevent accidental mistakes & be more descriptive in the way a variable is used. Yes, casting away const is bad - but any language with pointers can eventually do it. If it were implemented how I'm suggesting there would be no need to cast it away. If you did you would actually be blatantly and intentionally doing something wrong. Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | I didn't fully understand what you were describing before - I think I do now. My initial thought is that your concept is superior, but may take more compiler effort. I also don't like the idea of extra runtime overhead. Once again though, we need Walter to put forth _something_ about what he is thinking before we hash out the details. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ben Hinkle | On Thu, 30 Jun 2005 12:54:18 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote: >>> or >>> void foo(char[] x){ x[1] = 'd'; } >> >> error 'in' parameters are readonly. >> >>> char[] p = f.get(); >>> foo(p); > > OK. And I assume the readonly flag gets cleared if passed to an inout or out parameter? As 'out' yes (it's initialised as false), as 'inout' no that would be an error (passing readonly as mutable). >>> I think it would need to be carried around as a different type. >> >> Most cases can be handled at compile time without that. > > Is there a compile-time mechanism to check for this flag?As a code > maintainer how can one tell if the flag is set or not? That might be useful. I'm not sure whether it's required, after all you'll get errors from the compiler if you violate it (that's telling you right there). >> The case above that cannot, why does it need to be a seperate type? I dont >> think it does, it's the same type with a readonly bit set to true (rather >> than false). This bit can be checked at runtime, and disabled by -release. > > I'm not sure how a compile-time bit can be checked at run-time. Where is the bit stored during runtime? If a runtime bit is required, it doesn't have to be the *same* bit as used during compile. That is entirely up to the implementors of the compiler. It does have to be set based on the compile time bit however. The runtime bit would be part of every single object. (yes, I hear you gasp, that's a lot of extra bits, which is why it's a design time feature and disabled by a flag or -release or whatever) >> One goal IMO is to remove the need for us to tell the compiler it's >> readonly when it can clearly tell all by itself (in many cases) and verify >> for itself in others (runtime readonly flag). > > I like the idea that the compiler (and/or dlint) can warn (or possibly error > if the situation is completely unambiguous) when it thinks a readonly value > is being modified. I've suggested before that dlint include flow analysis to > warn about possible COW violations and other stuff like that. Runtime checks > should be avoided, though. I agree, but, if it's DBC system and disabled with -release and/or a seperate compiler flag then you can 'avoid' them. I see them as the same sort of thing as array bounds checking (tho they differ because readonly is permanant data for every object rather than a check that can be done when required). >> It has to be implicit to work. It's not bug tolerant otherwise. > > This is slightly OT but the phrase "bug tolerant" to me means the language tolerates bugs - which sounds like "bugs are ok". Of course I know this > isn't what you mean by "bug tolerant" (I hope) but it's what I think of > every time I read it. Ok, time to change my phrase. (I meant "bug tolerant" as in programming in the language was bug tolerant, not the language itself was bug tolerant) How about: "bug preventative" >>> It is non-trivial making an 'in' parameter into 'inout' since 'inout' >>> requires an lvalue while 'in' simply requires an rvalue. >> >> I can see how this would be painful for value types, but why is it painful >> for references (can I have an example?). > > With > void foo(inout char[] x) { ... } > char[] bar() { ... } > it is not legal to write > foo(bar()); True. > Instead one has to write > char[] temp = bar(); > foo(temp); > The output of bar is not an lvalue. Ok, I dont really think that is a problem, because: A. If you have a function: void foo(char[] x) { x[0] = 's'; } and someone is passing a rvalue into it, what is the point? I mean, they're not going to see the modification on the outside, in which case I assume you're using the change internally for something. If so, IMO the function should be written: void foo(char[] x) { char[] y = x.dup; } In order to be safe and to hold to it's design contract of only reading it's inputs. B. If you have the function: void foo(char[] x) { x[0] = 's'; } and the intention is to see the changes on the outside then it needs to be rewritten to: void foo(inout char[] x) { x[0] = 's'; } and you can't pass an lvalue to that, nor would you want to. Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Beveridge | On Thu, 30 Jun 2005 14:41:57 -0700, Brad Beveridge <brad@somewhere.net> wrote: > I didn't fully understand what you were describing before - I think I do now. Yay :) > My initial thought is that your concept is superior, but may take more compiler effort. Indeed, but that's its job IMO ;) More effort once, less effort x times where x is the number of people writing in D for the forseeable future. > I also don't like the idea of extra runtime overhead. Neither, but I think most of it can be done at compile time, the little runtime checking that remains can be disabled with a compile time flag after developement and before release. > Once again though, we need Walter to put forth _something_ about what he is thinking before we hash out the details. Yep, he holds the deciding vote (the only vote really). Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | On Fri, 01 Jul 2005 09:47:50 +1200, Regan Heath <regan@netwin.co.nz> wrote:
> On Thu, 30 Jun 2005 12:54:18 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote:
>>>> or
>>>> void foo(char[] x){ x[1] = 'd'; }
>>>
>>> error 'in' parameters are readonly.
>>>
>>>> char[] p = f.get();
>>>> foo(p);
>>
>> OK. And I assume the readonly flag gets cleared if passed to an inout or out parameter?
>
> As 'out' yes (it's initialised as false), as 'inout' no that would be an error (passing readonly as mutable).
>
>>>> I think it would need to be carried around as a different type.
>>>
>>> Most cases can be handled at compile time without that.
>>
>> Is there a compile-time mechanism to check for this flag?As a code
>> maintainer how can one tell if the flag is set or not?
>
> That might be useful. I'm not sure whether it's required, after all you'll get errors from the compiler if you violate it (that's telling you right there).
>
>>> The case above that cannot, why does it need to be a seperate type? I dont
>>> think it does, it's the same type with a readonly bit set to true (rather
>>> than false). This bit can be checked at runtime, and disabled by -release.
>>
>> I'm not sure how a compile-time bit can be checked at run-time. Where is the bit stored during runtime?
>
> If a runtime bit is required, it doesn't have to be the *same* bit as used during compile. That is entirely up to the implementors of the compiler. It does have to be set based on the compile time bit however. The runtime bit would be part of every single object. (yes, I hear you gasp, that's a lot of extra bits, which is why it's a design time feature and disabled by a flag or -release or whatever)
>
>>> One goal IMO is to remove the need for us to tell the compiler it's
>>> readonly when it can clearly tell all by itself (in many cases) and verify
>>> for itself in others (runtime readonly flag).
>>
>> I like the idea that the compiler (and/or dlint) can warn (or possibly error
>> if the situation is completely unambiguous) when it thinks a readonly value
>> is being modified. I've suggested before that dlint include flow analysis to
>> warn about possible COW violations and other stuff like that. Runtime checks
>> should be avoided, though.
>
> I agree, but, if it's DBC system and disabled with -release and/or a seperate compiler flag then you can 'avoid' them. I see them as the same sort of thing as array bounds checking (tho they differ because readonly is permanant data for every object rather than a check that can be done when required).
>
>>> It has to be implicit to work. It's not bug tolerant otherwise.
>>
>> This is slightly OT but the phrase "bug tolerant" to me means the language tolerates bugs - which sounds like "bugs are ok". Of course I know this
>> isn't what you mean by "bug tolerant" (I hope) but it's what I think of
>> every time I read it.
>
> Ok, time to change my phrase. (I meant "bug tolerant" as in programming in the language was bug tolerant, not the language itself was bug tolerant) How about: "bug preventative"
>
>>>> It is non-trivial making an 'in' parameter into 'inout' since 'inout'
>>>> requires an lvalue while 'in' simply requires an rvalue.
>>>
>>> I can see how this would be painful for value types, but why is it painful
>>> for references (can I have an example?).
>>
>> With
>> void foo(inout char[] x) { ... }
>> char[] bar() { ... }
>> it is not legal to write
>> foo(bar());
>
> True.
>
>> Instead one has to write
>> char[] temp = bar();
>> foo(temp);
>> The output of bar is not an lvalue.
>
> Ok, I dont really think that is a problem, because:
>
> A. If you have a function:
>
> void foo(char[] x) { x[0] = 's'; }
>
> and someone is passing a rvalue into it, what is the point? I mean, they're not going to see the modification on the outside, in which case I assume you're using the change internally for something. If so, IMO the function should be written:
>
> void foo(char[] x) {
> char[] y = x.dup;
> }
>
> In order to be safe and to hold to it's design contract of only reading it's inputs.
>
> B. If you have the function:
>
> void foo(char[] x) { x[0] = 's'; }
>
> and the intention is to see the changes on the outside then it needs to be rewritten to:
>
> void foo(inout char[] x) { x[0] = 's'; }
>
> and you can't pass an lvalue to that, nor would you want to.
Ooops, meant rvalue here.
Regan
|
Copyright © 1999-2021 by the D Language Foundation