Opened 13 years ago
Closed 13 years ago
#3407 closed Bugs (wontfix)
boost::call_once not re-entrant (at least in win32)
Reported by: | Owned by: | Anthony Williams | |
---|---|---|---|
Milestone: | Boost 1.41.0 | Component: | thread |
Version: | Boost 1.40.0 | Severity: | Problem |
Keywords: | threads | Cc: |
Description
The win32 implementation of boost::call_once ( boost/thread/win32/once.hpp version 1.40.0 ) is not reentrant for the same thread. The problem happens on line 127. The function is called, and then the flag is set. This order should be reversed, the flag should be set and then the function should be called. If the function throws, the flag must be set back.
This program demonstrates the issue:
#include <boost/thread/once.hpp>
static boost::once_flag flag;
void called_twice() {
boost::call_once( called_twice, flag );
gets here twice
}
int main() {
boost::call_once( called_twice, flag );
}
While this may seem like a contrived example, it can easily happen in practice. Consider the case of a boost::singleton constructor that calls a function which then accesses its 'instance' member. The result will be that the singleton is created twice, because call_once is not re-entrant.
Also consider the case of two singletons, A and B. A calls methods on B from its constructor, and B calls methods on A from its constructor. A will be created twice due to the non-reentrant behavior of call_once.
I have not studied the behavior of call_once for other platforms.
Change History (4)
comment:1 by , 13 years ago
comment:2 by , 13 years ago
As this works as expected, Anthony could just add on the documentation why re-entrant call_once functions are not a good idea? The explanation of Steven could be a good starting point.
comment:3 by , 13 years ago
Component: | threads → thread |
---|
comment:4 by , 13 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
Documentation updated, changeset 57862
The flag must be set after the function is called. The reason is that if another thread calls call_once, it should block until the function is completed. I'll also point out that there is no good behavior for a recursive call to call_once. If it calls the function again, then f will be called twice. If it does not, then whatever initialization that call_once is doing may be incomplete. In either case, you have to know whether a call can be recursive and carefully design for it. If it happens accidentally, you're probably toast regardless of how it behaves. In the face of recursive calls it is impossible to maintain the post-condition that the function has run exactly once.