- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello all, x, for example does not compiles ing++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 with:
I'm trying to use lazy initialization from Design Patterns manual. This the code example (p. 36):
[cpp]templateFirst, Mutex* mut seems to be redundant here. Second, instantiation of lazyclass lazy { tbb::atomic value; Mutex* mut; public: lazy() : value(NULL) {} ~lazy() {delete value;} T& get() { if( !value ) { T* tmp = new T(); if( value.compare_and_swap(tmp,NULL)!=NULL ) // Another thread installed the value, so throw away mine. delete tmp; } return value; } };[/cpp]
[plain]main.cpp: In constructor lazy::lazy() [with T = int]: main.cpp:30: instantiated from here main.cpp:10: error: no matching function for call to tbb::atomic ::atomic(NULL) ...../tbb/atomic.h:314: note: candidates are: tbb::atomic ::atomic(const tbb::atomic &) ...../tbb/atomic.h:314: note: tbb::atomic ::atomic()[/plain]
Thanks for help
EDITED:
Found that this compiles:
[cpp]templateclass lazy { tbb::atomic value; public: lazy(){value=NULL;} ~lazy() {delete value;} T& get() { if( !value ) { T* tmp = new T(); if( value.compare_and_swap(tmp,NULL)!=NULL ) // Another thread installed the value, so throw away mine. delete tmp; } return *value.operator->(); } };[/cpp]
Changes on lines 5 and 16.
Please confirm if this is correct usage.
Link Copied
13 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The mutex was left over from the blocking variant and is indeed not needed here. Strangely, the non-blocking version does remove an asterisk from the return statement where it shouldn't, so how about just restoring "return *value;" instead (I didn't check, though)? Using the NULL argument for atomic initialisation seems like a rookie mistake (see "Why atomic Has No Constructors" in the Tutorial), but the atomic can still be in the initialisation list instead ("lazy():value() {}"), which is generally preferable.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for letting us know about the issue.
Yes, such a way to initialize an atomic variable is correct. Since atomic deliberately misses any constructor (in order to be applicable in some sophisticated scenarios), it cannot be assigned a value via the initialization list.
An equivalent code is lazy() : value() {}. In this case, the C++ standard requires value to be zero-initialized.
Yes, such a way to initialize an atomic variable is correct. Since atomic
An equivalent code is lazy() : value() {}. In this case, the C++ standard requires value to be zero-initialized.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Alexey Kukanov
No problem. Just for the sake of knowledge, why is that the standard requires value to be zero-initialized? Are some static variables involved under the hood?
Regards,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Sorry, I wrote default-initialisation, but it's value-initialisation, which then through recursion implies zero-initialisation on the data member that represents the atomic's value. Any presence of static member variables would be irrelevant here.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
How about making"lazy" noncopyable?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This would be more appropriately called a deferred allocation.
Also, consider this change to your code:
T&get(){
if(!value){
if(value.compare_and_swap(1,NULL)==NULL) {
value=newT(); }
}
while((intptr_t)value == 1)
_mm_pause();
return*value.operator->();
}
The above is untested.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Now to convert to a "lazy" new, assuming that the ctor of T is expensive. Assume you want to create a lot of these T objects and you do not wish to wait until all these objects are created before you start to use them (some of them).
The lazyobjectcan place the reference or pointerto itself into a concurrent vector (call it a lazy ctor queue)and if first fill initiate an asynchronous task to empty and 'new' the items in the lazy ctor queue. As this asynchronous task runs, other tasks can manipulate the objects that have been constructed. The lazy new task can also monitor the number of remaining new items, and if above a threshold, it can parallel_invoke to increase the number of threads performing the lazy news.
Jim Dempsey
The lazyobjectcan place the reference or pointerto itself into a concurrent vector (call it a lazy ctor queue)and if first fill initiate an asynchronous task to empty and 'new' the items in the lazy ctor queue. As this asynchronous task runs, other tasks can manipulate the objects that have been constructed. The lazy new task can also monitor the number of remaining new items, and if above a threshold, it can parallel_invoke to increase the number of threads performing the lazy news.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, but in this case I need only one.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello all,
I think there still may be a problem if the object is destroyed. Then the lazy initializer won't know about it because its pointer has not been nullified, and thus will return pointer to destroyed object.
I think this bug currently ruins my code, but I could not think of any simple example to paste here.
Daniel
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"I think there still may be a problem if the object is destroyed."
That's no different from deleting a referent behind a smart pointer's back: don't do that.
That's no different from deleting a referent behind a smart pointer's back: don't do that.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Raf Schietekat
But eventually, every object will be destroyed, and it may be that another object will survive longer than that managed by lazy initializer and the former might ask for a pointer to the latter.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
From your comments a section of your code code calls a ctor that evokes a lazy initializer, then your section of codeis going to use the object immediately following the ctor.
However, the functional intent of a lazy initializer would be to permit the ctor(s) to run concurrently with the section of code which issued the lazy initializer and after starting the lazy initializer, your code following the ctor would continue torun doing some useful work up until it needed to use the object(s) being lazy initialized. And at which point would block if initialization not (or until)complete. Should this interviening code decide to exit prior to completion of the lazy initialization, you have a rightful concern that holder of the pointer/reference will "evaporate" prior to completion of the lazy initialization. It is your responsibility to write your dtor such that this will not happen.
Jim Dempsey
However, the functional intent of a lazy initializer would be to permit the ctor(s) to run concurrently with the section of code which issued the lazy initializer and after starting the lazy initializer, your code following the ctor would continue torun doing some useful work up until it needed to use the object(s) being lazy initialized. And at which point would block if initialization not (or until)complete. Should this interviening code decide to exit prior to completion of the lazy initialization, you have a rightful concern that holder of the pointer/reference will "evaporate" prior to completion of the lazy initialization. It is your responsibility to write your dtor such that this will not happen.
Jim Dempsey
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page