Topic: resource locks and lifetime of temporaries
Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1996/11/11 Raw View
walker@island.com (Richard J. Walker) writes:
> kanze@gabi-soft.fr (J. Kanze) wrote:
> >Ignoring the errors in your examples (and I think that the
> >misinterpretation of "Lock(rs)" would affect a significant number of
> >programmers), your question is basically: what is the lifetime of a
> >temporary? The answer is: until the end of the enclosing full
> >expression. While there are a few subtilities for special cases, at the
> >very latest, the destructor of a temporary will be called at the
> >semi-colon terminating the statement.
>
> Given the error you pointed out, this reply misses the point.
>
> The temporary in question is called "lock", and it is guaranteed to
> exist until the end of the enclosing block, i.e. the matching } .
I don't think that this was the question. The initial poster asked
about *replacing* the named temporary with an unnamed one (since he had
no use for the name).
> The temporary lock object contains only one reference, i.e. no real
> member data, but indeed you can rely on the dtor being called
> at the end of the scope and not before.
>
> Without this guarantee, extremely useful classlets like the above
> are not possible.
>
> The only question in my mind is the lifetime of _unnamed_ temporaries.
>
> I know this was the cause of nasty bugs for us using VC++4.0,
> which evidently will create and destroy the unnamed temporary _before_
> the end of the enclosing scope. We avoid this by providing various
> macros which make it impossible to forget to name the temporary.
This is, however, what the language requires. Unnamed temporaries are
destructed at the end of the enclosing full expression.
--
James Kanze +33 3 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: walker@island.com (Richard J. Walker)
Date: 1996/11/04 Raw View
kanze@gabi-soft.fr (J. Kanze) wrote:
>"ian (i.) willmott" <willmott@nortel.ca> writes:
>> Suppose you have the following template class, intended for locking
>> resource objects which have lock() and unlock() methods:
>>
>> template<class T> class Lock {
>> T &x;
>> public:
>> Lock(T &xx):
>> x(xx) { x.lock(); }
>> ~Lock()
>> { x.unlock(); }
>> };
>>
>> You use it like this:
>>
>> MyResource rs;
>>
>> {
>> Lock lock(rs); // object is never referenced; we only care
>> // about side effects of ctor and dtor
>I presume you mean:
> Lock< MyResource > lock( rs ) ;
Yes, I believe this is what he meant.
>Or perhaps, that Lock should not be a template class.
Of course it should be, if you want a reuseable "locker" class.
>>
>> // access resource rs here
>>
>> // rs is unlocked at end of block
>> }
>>
>> This is pretty straightforward, but the identifier "lock" is redundant
>> because it is never referenced after the declaration. Can it be
>> eliminated?
Eliminating the "lock" identifier makes it an unnamed temporary.
See below.
>>
>> {
>> Lock(rs); // not a declaration?
>If Lock is a template, this suffers from the same problem as above.
>If Lock is not a template, this declares an instance of the variable
>with the name "rs", initialized using the default constructor. If there
>is no default constructor, the program is illegal.
>> // ...
>> }
>>
>> If I'm not mistaken, this is no longer a declaration but a constructor
>> invocation statement which creates a temporary value.
>You are mistake. This is a declaration of a variable with the name "rs".
>> The question is,
>> can you rely on that value being destroyed along with named local
>> variables at the end of the block, in the usual reverse order, or is the
>> compiler allowed to destroy it anywhere between the constructor call and
>> the closing brace? If so, it's not safe to do this and the seemingly
>> redundant identifier is in fact necessary, along with the inevitable
>> "variable declared but never used" compiler warning. What's the relevant
>> rule?
>Ignoring the errors in your examples (and I think that the
>misinterpretation of "Lock(rs)" would affect a significant number of
>programmers), your question is basically: what is the lifetime of a
>temporary? The answer is: until the end of the enclosing full
>expression. While there are a few subtilities for special cases, at the
>very latest, the destructor of a temporary will be called at the
>semi-colon terminating the statement.
Given the error you pointed out, this reply misses the point.
The temporary in question is called "lock", and it is guaranteed to
exist until the end of the enclosing block, i.e. the matching } .
The temporary lock object contains only one reference, i.e. no real
member data, but indeed you can rely on the dtor being called
at the end of the scope and not before.
Without this guarantee, extremely useful classlets like the above
are not possible.
The only question in my mind is the lifetime of _unnamed_ temporaries.
I know this was the cause of nasty bugs for us using VC++4.0,
which evidently will create and destroy the unnamed temporary _before_
the end of the enclosing scope. We avoid this by providing various
macros which make it impossible to forget to name the temporary.
BTW, to emphasize the nature of the classlet and temporary,
I would call them "Locker" and "locker".
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: ken@digitas.org (Ken Shan)
Date: 1996/11/06 Raw View
Richard J. Walker (walker@island.com) wrote:
> The only question in my mind is the lifetime of _unnamed_ temporaries.
> I know this was the cause of nasty bugs for us using VC++4.0,
> which evidently will create and destroy the unnamed temporary _before_
> the end of the enclosing scope. We avoid this by providing various
> macros which make it impossible to forget to name the temporary.
I don't think this is a bug. Unnamed temporaries are destroyed at the
end of the enclosing full expression which in this case is at the end
of the semicolon on the same line.
--
blue | Ken; Shan, Chung-chieh; Sian7, Tiong1-kiat8; ccshan@fas.harvard.edu.
() | Your code today becomes the mind tomorrow: Your plan its means,
/\ | your dream its ends, your ideal its elegance. Hack on.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Chelly Green <chelly@eden.com>
Date: 1996/10/24 Raw View
Bruce Visscher wrote:
>
[named temporaries cannot be optimized away]
> > If so, it's not safe to do this and the seemingly
> > redundant identifier is in fact necessary, along with the inevitable
> > "variable declared but never used" compiler warning.
>
> You are correct. The seemingly redundant identifier *is* necessary.
> I agree that any such warning would be an annoyance in this context.
> If your compiler vendor doesn't allow you to disable the warning
> (which is certainly not required in this case), I would complain.
The compiler I use only warns about unused objects of built-in types or
classes/structs without constructors or destructors.
...
> I used to dislike this rule for exactly such problems. However, I
> have since discovered that it be can useful under certain
> circumstances.
>
> I recently developed a library component which encapsulates a legacy
> API that is known not to be reentrant. In order to make it thread
> safe I discovered that I could do the following. I've adapted the
> original design (from memory) to use your class (which I actually like
> better than the one I wrote:-) in this is simplified sketch.
>
> void ApiCall(void* apiHandle);
>
> class Api {
> private:
> //...
> auto_ptr<Lock<void*> > GetApiHandle();
> //...
> public:
> //...
> void f();
> //...
> };
>
> void Api::f() {
> //...
> ApiCall(GetApiHandle().Get()); // added T& Lock<T>::Get() accessor
> //...
> }
>
> My understanding of the lifetime of temporaries rule is that the lock
> will be created just before and be destroyed just after the call since
> I believe the "full expression" in this case is the function call
> (Experts, please check this!).
Yes, I would like a definite OK on this too! :-)
> Note that I can now have multiple API
> calls within the same scope without a deadlock. Also, multi-threaded
> performance may be improved somewhat since the lock exists for the
> shortest time possible (though I wonder how this compares to the
> overhead of the heap allocation which I've added).
Why, then, does there need to be a heap allocation?
Assuming the Lock object can't be copied, then would this work?
class GetAPIHandle : public Lock<void*> {
public:
GetAPIHandle();
};
void Api::f() {
//...
ApiCall( GetApiHandle().Get() );
//...
}
No freestore allocation or extra overhead.
--
Chelly Green | chelly@eden.com | C++ - http://www.eden.com/~chelly
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1996/10/28 Raw View
"ian (i.) willmott" <willmott@nortel.ca> writes:
> Suppose you have the following template class, intended for locking
> resource objects which have lock() and unlock() methods:
>
> template<class T> class Lock {
> T &x;
> public:
> Lock(T &xx):
> x(xx) { x.lock(); }
> ~Lock()
> { x.unlock(); }
> };
>
> You use it like this:
>
> MyResource rs;
>
> {
> Lock lock(rs); // object is never referenced; we only care
> // about side effects of ctor and dtor
I presume you mean:
Lock< MyResource > lock( rs ) ;
Or perhaps, that Lock should not be a template class.
>
> // access resource rs here
>
> // rs is unlocked at end of block
> }
>
> This is pretty straightforward, but the identifier "lock" is redundant
> because it is never referenced after the declaration. Can it be
> eliminated?
>
> {
> Lock(rs); // not a declaration?
If Lock is a template, this suffers from the same problem as above.
If Lock is not a template, this declares an instance of the variable
with the name "rs", initialized using the default constructor. If there
is no default constructor, the program is illegal.
> // ...
> }
>
> If I'm not mistaken, this is no longer a declaration but a constructor
> invocation statement which creates a temporary value.
You are mistake. This is a declaration of a variable with the name "rs".
> The question is,
> can you rely on that value being destroyed along with named local
> variables at the end of the block, in the usual reverse order, or is the
> compiler allowed to destroy it anywhere between the constructor call and
> the closing brace? If so, it's not safe to do this and the seemingly
> redundant identifier is in fact necessary, along with the inevitable
> "variable declared but never used" compiler warning. What's the relevant
> rule?
Ignoring the errors in your examples (and I think that the
misinterpretation of "Lock(rs)" would affect a significant number of
programmers), your question is basically: what is the lifetime of a
temporary? The answer is: until the end of the enclosing full
expression. While there are a few subtilities for special cases, at the
very latest, the destructor of a temporary will be called at the
semi-colon terminating the statement.
--
James Kanze +33 3 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "ian (i.) willmott" <willmott@nortel.ca>
Date: 1996/10/22 Raw View
Subject: resource locks and lifetime of temporaries
Suppose you have the following template class, intended for locking
resource objects which have lock() and unlock() methods:
template<class T> class Lock {
T &x;
public:
Lock(T &xx):
x(xx) { x.lock(); }
~Lock()
{ x.unlock(); }
};
You use it like this:
MyResource rs;
{
Lock lock(rs); // object is never referenced; we only care
// about side effects of ctor and dtor
// access resource rs here
// rs is unlocked at end of block
}
This is pretty straightforward, but the identifier "lock" is redundant
because it is never referenced after the declaration. Can it be
eliminated?
{
Lock(rs); // not a declaration?
// ...
}
If I'm not mistaken, this is no longer a declaration but a constructor
invocation statement which creates a temporary value. The question is,
can you rely on that value being destroyed along with named local
variables at the end of the block, in the usual reverse order, or is the
compiler allowed to destroy it anywhere between the constructor call and
the closing brace? If so, it's not safe to do this and the seemingly
redundant identifier is in fact necessary, along with the inevitable
"variable declared but never used" compiler warning. What's the relevant
rule?
Ian Willmott
Northern Telecom
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: bvisscher@spyder.net (Bruce Visscher)
Date: 1996/10/23 Raw View
On Fri, 18 Oct 1996 16:07:51 -0400, "ian (i.) willmott"
<willmott@nortel.ca> wrote:
> Suppose you have the following template class, intended for locking
> resource objects which have lock() and unlock() methods:
>
> template<class T> class Lock {
> T &x;
> public:
> Lock(T &xx):
> x(xx) { x.lock(); }
> ~Lock()
> { x.unlock(); }
> };
>
> You use it like this:
>
> MyResource rs;
>
> {
> Lock lock(rs); // object is never referenced; we only care
> // about side effects of ctor and dtor
>
> // access resource rs here
>
> // rs is unlocked at end of block
> }
>
> This is pretty straightforward, but the identifier "lock" is redundant
> because it is never referenced after the declaration. Can it be
> eliminated?
Since the constructor and destructor have side-effects, it cannot be
eliminated. From the January 1996 DWP:
> 3.7.2 Automatic storage duration [basic.stc.auto]
[...]
> 3 If a named automatic object has initialization or a destructor with
> side effects, it shall not be destroyed before the end of its block,
> nor shall it be eliminated as an optimization even if it appears to be
> unused.
>
> {
> Lock(rs); // not a declaration?
>
> // ...
> }
>
> If I'm not mistaken, this is no longer a declaration but a constructor
> invocation statement which creates a temporary value.
I would call it a declaration of an unnamed temporary(?).
>From the DWP:
> 12.1 Constructors [class.ctor]
[...]
> 12A constructor can be used explicitly to create new objects of its
> type, using the syntax
> class-name ( expression-listopt )
> [Example:
> complex zz = complex(1,2.3);
> cprint( complex(7.8,1.2) );
> --end example] An object created in this way is unnamed. [Note:
> _class.temporary_ describes the lifetime of temporary objects. ]
> The question is,
> can you rely on that value being destroyed along with named local
> variables at the end of the block, in the usual reverse order, or is the
> compiler allowed to destroy it anywhere between the constructor call and
> the closing brace?
My understanding is the compiler is required to destroy it immediately
after it has been created in this case.
> If so, it's not safe to do this and the seemingly
> redundant identifier is in fact necessary, along with the inevitable
> "variable declared but never used" compiler warning.
You are correct. The seemingly redundant identifier *is* necessary.
I agree that any such warning would be an annoyance in this context.
If your compiler vendor doesn't allow you to disable the warning
(which is certainly not required in this case), I would complain.
> What's the relevant rule?
Again from the Januray 1996 DWP:
> 12.2 Temporary objects [class.temporary]
[...]
> 3 [...]Temporary objects are destroyed as the last step
> in evaluating the full-expression (_intro.execution_) that (lexically)
> contains the point where they were created. This is true even if that
> evaluation ends in throwing an exception.
There are some exceptions and clarifications to this rule in the
paragraphs that follow.
There's a good discussion on this in "Design and Evolution of C++" by
Bjarne Stroustrup.
I used to dislike this rule for exactly such problems. However, I
have since discovered that it be can useful under certain
circumstances.
I recently developed a library component which encapsulates a legacy
API that is known not to be reentrant. In order to make it thread
safe I discovered that I could do the following. I've adapted the
original design (from memory) to use your class (which I actually like
better than the one I wrote:-) in this is simplified sketch.
void ApiCall(void* apiHandle);
class Api {
private:
//...
auto_ptr<Lock<void*> > GetApiHandle();
//...
public:
//...
void f();
//...
};
void Api::f() {
//...
ApiCall(GetApiHandle().Get()); // added T& Lock<T>::Get() accessor
//...
}
My understanding of the lifetime of temporaries rule is that the lock
will be created just before and be destroyed just after the call since
I believe the "full expression" in this case is the function call
(Experts, please check this!). Note that I can now have multiple API
calls within the same scope without a deadlock. Also, multi-threaded
performance may be improved somewhat since the lock exists for the
shortest time possible (though I wonder how this compares to the
overhead of the heap allocation which I've added).
Bruce
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: "ian (i.) willmott" <willmott@nortel.ca>
Date: 1996/10/21 Raw View
Suppose you have the following template class, intended for locking
resource objects which have lock() and unlock() methods:
template<class T> class Lock {
T &x;
public:
Lock(T &xx):
x(xx) { x.lock(); }
~Lock()
{ x.unlock(); }
};
You use it like this:
MyResource rs;
{
Lock lock(rs); // object is never referenced; we only care
// about side effects of ctor and dtor
// access resource rs here
// rs is unlocked at end of block
}
This is pretty straightforward, but the identifier "lock" is redundant
because it is never referenced after the declaration. Can it be
eliminated?
{
Lock(rs); // not a declaration?
// ...
}
If I'm not mistaken, this is no longer a declaration but a constructor
invocation statement which creates a temporary value. The question is,
can you rely on that value being destroyed along with named local
variables at the end of the block, in the usual reverse order, or is the
compiler allowed to destroy it anywhere between the constructor call and
the closing brace? If so, it's not safe to do this and the seemingly
redundant identifier is in fact necessary, along with the inevitable
"variable declared but never used" compiler warning. What's the relevant
rule?
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]