[thread] Can Boost.Thread use Boost.Atomic without falling on a compatibility issue?

Hi, in order to fix this issue, https://svn.boost.org/trac/boost/ticket/5752 : "boost::call_once() is unreliable on some platforms", I would like to use Boost.Atomic as suggested on the ticket. As Boost.Atomic is not a header-only library the user would need to link with boost_atomic. Is this an acceptable change? I'm requesting this as recently there were some people reactive to some changes in Boost.Thread. Would this be considered an compatibility issue? If yes should the fix for the call_once issue be included if the user request it using conditional compilation or even go on a separated version of Boost.Thread? Thanks, Vicente

On Saturday 12 January 2013 11:37:30 Vicente J. Botet Escriba wrote:
Hi,
in order to fix this issue, https://svn.boost.org/trac/boost/ticket/5752
: "boost::call_once() is unreliable on some platforms", I would like to
use Boost.Atomic as suggested on the ticket. As Boost.Atomic is not a header-only library the user would need to link with boost_atomic.
Is this an acceptable change? I'm requesting this as recently there were some people reactive to some changes in Boost.Thread. Would this be considered an compatibility issue? If yes should the fix for the call_once issue be included if the user request it using conditional compilation or even go on a separated version of Boost.Thread?
I think people were mostly concerned with functionality changes of Boost.Thread while the suggested switch to Boost.Atomic seems more of an implementation issue. IMHO, the switch to Boost.Atomic is a good idea; we should localize the atomic code in one place. Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag.

Le 12/01/13 11:51, Andrey Semashev a écrit :
On Saturday 12 January 2013 11:37:30 Vicente J. Botet Escriba wrote:
Hi,
in order to fix this issue, https://svn.boost.org/trac/boost/ticket/5752
: "boost::call_once() is unreliable on some platforms", I would like to
use Boost.Atomic as suggested on the ticket. As Boost.Atomic is not a header-only library the user would need to link with boost_atomic.
Is this an acceptable change? I'm requesting this as recently there were some people reactive to some changes in Boost.Thread. Would this be considered an compatibility issue? If yes should the fix for the call_once issue be included if the user request it using conditional compilation or even go on a separated version of Boost.Thread? I think people were mostly concerned with functionality changes of Boost.Thread while the suggested switch to Boost.Atomic seems more of an implementation issue. IMHO, the switch to Boost.Atomic is a good idea; we should localize the atomic code in one place.
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag.
yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic? Or, can Boost.Atomic be header-only? Best, Vicente

On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Le 12/01/13 11:51, Andrey Semashev a écrit :
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag.
yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic?
I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it.
Or, can Boost.Atomic be header-only?
It can't be completely header-only because it requires a spinkock pool singleton. You can find the discussion about it in the nearby thread "[boost] [atomic] [release] possible linking problem with atomic" and other older threads.

Le 12/01/13 14:26, Andrey Semashev a écrit :
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag. yes, this will be great. I don't know Boost.Atomic details to try to do
Le 12/01/13 11:51, Andrey Semashev a écrit : this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic? I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it.
Or, can Boost.Atomic be header-only? It can't be completely header-only because it requires a spinkock pool singleton. You can find the discussion about it in the nearby thread "[boost] [atomic] [release] possible linking problem with atomic" and other older threads.
Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and libs/thread/src/pthread/once.hpp? Could you send the resulting files also? Thanks again, Vicente

On Saturday 12 January 2013 15:48:21 Vicente J. Botet Escriba wrote:
Le 12/01/13 14:26, Andrey Semashev a écrit :
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Le 12/01/13 11:51, Andrey Semashev a écrit :
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag.
yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic?
I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it.
Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and libs/thread/src/pthread/once.hpp? Could you send the resulting files also?
There is no libs/thread/src/pthread/once.hpp file as far as I can see. The boost/thread/pthread/once.hpp file is no longer needed and can be removed (as well as boost/thread/win32/once.hpp when win32 version is implemented). The complete public code is platform-independent and resides in boost/thread/once.hpp after the patch is applied.

Le 12/01/13 17:09, Andrey Semashev a écrit :
On Saturday 12 January 2013 15:48:21 Vicente J. Botet Escriba wrote:
Le 12/01/13 14:26, Andrey Semashev a écrit :
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag. yes, this will be great. I don't know Boost.Atomic details to try to do
Le 12/01/13 11:51, Andrey Semashev a écrit : this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic? I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it.
Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and libs/thread/src/pthread/once.hpp? Could you send the resulting files also? There is no libs/thread/src/pthread/once.hpp file as far as I can see. RIght. I meant
libs/thread/src/pthread/once.cpp
The boost/thread/pthread/once.hpp file is no longer needed and can be removed (as well as boost/thread/win32/once.hpp when win32 version is implemented). The complete public code is platform-independent and resides in boost/thread/once.hpp after the patch is applied.
IIRC the problem was on the Posix implementation, so a specific patch for the pthread files will be desirable. Anyway, could you send the resulting files to make easier the review? Best, Vicente

On Saturday 12 January 2013 17:49:38 Vicente J. Botet Escriba wrote:
Le 12/01/13 17:09, Andrey Semashev a écrit :
On Saturday 12 January 2013 15:48:21 Vicente J. Botet Escriba wrote:
Le 12/01/13 14:26, Andrey Semashev a écrit :
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Le 12/01/13 11:51, Andrey Semashev a écrit :
Anyway, can Boost.Thread be modified in such a way so that Boost.Atomic use is not exposed to the user? E.g. so that call_once invokes a compiled function implemented within Boost.Thread library that uses Boost.Atomic to modify the once flag.
yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic?
I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it.
Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and libs/thread/src/pthread/once.hpp? Could you send the resulting files also?
There is no libs/thread/src/pthread/once.hpp file as far as I can see.
RIght. I meant
libs/thread/src/pthread/once.cpp
It is patched.
The boost/thread/pthread/once.hpp file is no longer needed and can be removed (as well as boost/thread/win32/once.hpp when win32 version is implemented). The complete public code is platform-independent and resides in boost/thread/once.hpp after the patch is applied.
IIRC the problem was on the Posix implementation, so a specific patch for the pthread files will be desirable.
Ok, I intended to port both Windows and POSIX implementations but we could do it just for POSIX variant. I attached the updated patch to the ticket. The Jamfile will also have to be updated to add the dependency on Boost.Atomic. However, the Jamfile in Boost.Thread is rather complicated so I didn't do that.
Anyway, could you send the resulting files to make easier the review?
The files are attached. These should be boost/thread/posix/once.hpp and libs/thread/src/posix/once.cpp, other files are intact.

Le 12/01/13 18:29, Andrey Semashev a écrit :
On Saturday 12 January 2013 17:49:38 Vicente J. Botet Escriba wrote:
Le 12/01/13 17:09, Andrey Semashev a écrit :
On Saturday 12 January 2013 15:48:21 Vicente J. Botet Escriba wrote:
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Le 12/01/13 11:51, Andrey Semashev a écrit : > Anyway, can Boost.Thread be modified in such a way so that > Boost.Atomic > use is not exposed to the user? E.g. so that call_once invokes a > compiled > function implemented within Boost.Thread library that uses > Boost.Atomic > to modify the once flag. yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic? I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it. Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and
Le 12/01/13 14:26, Andrey Semashev a écrit : libs/thread/src/pthread/once.hpp? Could you send the resulting files also? There is no libs/thread/src/pthread/once.hpp file as far as I can see. RIght. I meant
libs/thread/src/pthread/once.cpp It is patched.
The boost/thread/pthread/once.hpp file is no longer needed and can be removed (as well as boost/thread/win32/once.hpp when win32 version is implemented). The complete public code is platform-independent and resides in boost/thread/once.hpp after the patch is applied. IIRC the problem was on the Posix implementation, so a specific patch for the pthread files will be desirable. Ok, I intended to port both Windows and POSIX implementations but we could do it just for POSIX variant.
I attached the updated patch to the ticket. The Jamfile will also have to be updated to add the dependency on Boost.Atomic. However, the Jamfile in Boost.Thread is rather complicated so I didn't do that.
Anyway, could you send the resulting files to make easier the review? The files are attached. These should be boost/thread/posix/once.hpp and libs/thread/src/posix/once.cpp, other files are intact.
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same. What I was asking for is just a patch to ensure that typedef unsigned long uintmax_atomic_t; is atomic in all platforms. For the comments about the Jamfile, I deduce that the patch need to link with Boost.Atomic. I expected a patch that didn't need to link to Boost.Atomic. I should have missed something. Could you confirm my very first analysis? Maybe you could describe your changes? Vicente

Anyway, could you send the resulting files to make easier the review? The files are attached. These should be boost/thread/posix/once.hpp and libs/thread/src/posix/once.cpp, other files are intact.
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same. What I was asking for is just a patch to ensure that
typedef unsigned long uintmax_atomic_t;
is atomic in all platforms.
i suppose this is the case for most platforms. however according to the c++11 standard only atomic_flag is guaranteed to be lockfree, unfortunately we cannot guarantee this for boost.atomic (without some additional platform-specific code).
For the comments about the Jamfile, I deduce that the patch need to link with Boost.Atomic. I expected a patch that didn't need to link to Boost.Atomic. I should have missed something.
again, this is something we cannot guarantee for boost.atomic. during the review the original author wanted to provide an implementation of atomic<> that is address-free that should have become part of boost.interprocess ... well, that was before he disappeared. that implementation could have been header-only, as every atomic<> would have been associated with a spin-lock (or every non-lockfree atomic<> if we decide not to care about binary compatibility) but i wonder: is it a problem to link boost.thread with boost.atomic? cheers, tim

Le 12/01/13 19:20, Tim Blechmann a écrit :
but i wonder: is it a problem to link boost.thread with boost.atomic?
This was the question I made on my first post. If users don't find that this is a breaking change I will just replace uintmax_atomic_t by atomic<uintmax_t> which seems simple of all the versions. If the boost community find that this is a breaking change I will fix it conditionally. This is the concern of my post. The Andrey's post let me thought that we could do something that doesn't change the way the users uses the library, but it seems that there are some cases that will need that user link with. Best, Vicente

On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same.
The algorithm is different for sure. What exactly do you mean by C++11 interface? Does C++11 have call_once? The only C++11 thing I saw was the once_flag definition which gave no apparent benefits.
What I was asking for is just a patch to ensure that
typedef unsigned long uintmax_atomic_t;
is atomic in all platforms.
Boost.Atomic only provides atomic<> template, it does not define the functional interface for atomic ops. The typedef you mentioned can only be the following with Boost.Atomic: typedef atomic< unsigned long > uintmax_atomic_t; The integer type can be different from unsigned long, depending on the platform-specific support macros, but you get the idea. This is *not* what I proposed because, obviously, this would require including Boost.Atomic into user's code and consequently linking with it. As a side note, the standard doesn't specify the underlying storage types for the atomic<> wrapper. Whenever you try to use atomic ops on actual integer types you are relying on platform-specific details, which Boost.Atomic is designed to conceal.
For the comments about the Jamfile, I deduce that the patch need to link with Boost.Atomic. I expected a patch that didn't need to link to Boost.Atomic. I should have missed something.
That's right. Again, my proposal was to use Boost.Atomic in Boost.Thread internally and not expose it to users. That way Boost.Thread links with Boost.Atomic, but users do not.

On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same.
The algorithm is different for sure. What exactly do you mean by C++11 interface? Does C++11 have call_once? The only C++11 thing I saw was the once_flag definition which gave no apparent benefits.
What I was asking for is just a patch to ensure that
typedef unsigned long uintmax_atomic_t;
is atomic in all platforms.
Boost.Atomic only provides atomic<> template, it does not define the functional interface for atomic ops. The typedef you mentioned can only be the following with Boost.Atomic:
typedef atomic< unsigned long > uintmax_atomic_t;
The integer type can be different from unsigned long, depending on the platform-specific support macros, but you get the idea. This is *not* what I proposed because, obviously, this would require including Boost.Atomic into user's code and consequently linking with it. This is what I was looking for as it is intended by the typedef name,
Le 12/01/13 19:50, Andrey Semashev a écrit : that is the larger unsigned integer type that can be atomic for a specific platform.
As a side note, the standard doesn't specify the underlying storage types for the atomic<> wrapper. Whenever you try to use atomic ops on actual integer types you are relying on platform-specific details, which Boost.Atomic is designed to conceal.
For the comments about the Jamfile, I deduce that the patch need to link with Boost.Atomic. I expected a patch that didn't need to link to Boost.Atomic. I should have missed something.
That's right. Again, my proposal was to use Boost.Atomic in Boost.Thread internally and not expose it to users. That way Boost.Thread links with Boost.Atomic, but users do not.
I don't see how Boost.Thread can use Boost.Atomic internally. What do you mean? linking statically? Shouldn't this generate a double definition on the executable if the application needs Boost.Atomic? Vicente

On January 13, 2013 12:42:34 AM "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
The integer type can be different from unsigned long, depending on the platform-specific support macros, but you get the idea. This is *not* what I proposed because, obviously, this would require including Boost.Atomic into user's code and consequently linking with it.
This is what I was looking for as it is intended by the typedef name, that is the larger unsigned integer type that can be atomic for a specific platform.
Like I said, you won't be able to select such a type without including Boost.Atomic in the user's code.
That's right. Again, my proposal was to use Boost.Atomic in Boost.Thread internally and not expose it to users. That way Boost.Thread links with Boost.Atomic, but users do not.
I don't see how Boost.Thread can use Boost.Atomic internally. What do you mean? linking statically? Shouldn't this generate a double definition on the executable if the application needs Boost.Atomic?
My patch demonstrates how this could be achieved. Boost.Thread should never link statically to any libs, not just Boost.Atomic. Here is why: 1. Linking only happen when you build the dynamic library of Boost.Thread. If an application uses the dynamic lib of Boost.Thread, chances are high that the application is multi-module, and you cannot know if it doesn't use Boost.Atomic or Boost.System on its own. If Boost.Thread links dynamically to other Boost libs, it is safe for the application to link dynamically with them as well if it uses them in its own code. If the application doesn't use Boost.Atomic on its own then it doesn't link to it. 2. When you build a static lib of Boost.Thread, you don't link. The resulting archive doesn't contain other Boost libs, it only refers to the symbols of those libs. The application can link Boost.Thread statically and other libs statically or dynamically, depending on its needs [1]. However, according to my practice, you either link all libraries statically (which is a typical case for monolithic applications) or you link everything dynamically. Mixed linking is a very rare case and is typically discouraged. Unfortunately, in the second case the application will have to link Boost.Atomic explicitly if Boost.Thread uses Boost.Atomic in a way that involves the spinlock pool (which is unlikely with my patch since it uses only 32 bit atomic ops). [1] This will not work on Windows because it mangles symbols differently when exporting from static or dynamic libs. But I don't see it as a big problem; everything will be fine as long as you follow the "all static or all dynamic" rule.

Le 12/01/13 19:50, Andrey Semashev a écrit :
On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same.
The algorithm is different for sure. What exactly do you mean by C++11 interface? Does C++11 have call_once? The only C++11 thing I saw was the once_flag definition which gave no apparent benefits.
C++11 defines the following interface. There is no STD_ONCE_INIT initializer. struct once_flag { constexpr once_flag() noexcept; once_flag(const once_flag&) = delete; once_flag& operator=(const once_flag&) = delete; }; template<class Callable, class ...Args> void call_once(once_flag& flag, Callable func, Args&&... args); Could this once_flag be implemented using the trick you used to mask the atomic dependency on the header file? Vicente

On Sunday 13 January 2013 00:15:23 Vicente J. Botet Escriba wrote:
Le 12/01/13 19:50, Andrey Semashev a écrit :
On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba"
<vicente.botet@wanadoo.fr> wrote:
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same.
The algorithm is different for sure. What exactly do you mean by C++11 interface? Does C++11 have call_once? The only C++11 thing I saw was the once_flag definition which gave no apparent benefits.
C++11 defines the following interface. There is no STD_ONCE_INIT initializer.
struct once_flag { constexpr once_flag() noexcept; once_flag(const once_flag&) = delete; once_flag& operator=(const once_flag&) = delete; }; template<class Callable, class ...Args> void call_once(once_flag& flag, Callable func, Args&&... args);
Could this once_flag be implemented using the trick you used to mask the atomic dependency on the header file?
Hmm, I didn't know the standard defined call_once. It is possible to define once_flag as defined by the standard. But the previous implementation seem incorrect to me. once_flag *must* be either POD or have a constexpr default constructor while the previous code (as well as the code in win32/once.hpp) uses BOOST_CONSTEXPR which can be empty. BOOST_THREAD_PROVIDES_ONCE_CXX11 macro doesn't depend on constexpr or noexcept availability so it can't be used to enable C++11 implementation. So in order to implement C++11 interface there has to be a macro that depends on both constexpr or noexcept availability. Then the once_flag definition should use constexpr and noexcept unconditionally.

Le 13/01/13 11:09, Andrey Semashev a écrit :
On Sunday 13 January 2013 00:15:23 Vicente J. Botet Escriba wrote:
Le 12/01/13 19:50, Andrey Semashev a écrit :
On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba"
Andrey, the provided patch goes too far for what I was expecting. You have made a lot of changes, C++11 interface has not been preserved, and I'm not sure the algorithm is the same. The algorithm is different for sure. What exactly do you mean by C++11 interface? Does C++11 have call_once? The only C++11 thing I saw was
<vicente.botet@wanadoo.fr> wrote: the once_flag definition which gave no apparent benefits. C++11 defines the following interface. There is no STD_ONCE_INIT initializer.
struct once_flag { constexpr once_flag() noexcept; once_flag(const once_flag&) = delete; once_flag& operator=(const once_flag&) = delete; }; template<class Callable, class ...Args> void call_once(once_flag& flag, Callable func, Args&&... args);
Could this once_flag be implemented using the trick you used to mask the atomic dependency on the header file? Hmm, I didn't know the standard defined call_once.
It is possible to define once_flag as defined by the standard.
But the previous implementation seem incorrect to me. once_flag *must* be either POD or have a constexpr default constructor while the previous code (as well as the code in win32/once.hpp) uses BOOST_CONSTEXPR which can be empty. BOOST_THREAD_PROVIDES_ONCE_CXX11 macro doesn't depend on constexpr or noexcept availability so it can't be used to enable C++11 implementation. Andrey, could you explain me why once_flag must be either be a POD or follow strictly the C++11 interface? In other words, what are the
Could you work on complementing your patch to take care of the C++11 interface? problems defining it as follows? struct once_flag { once_flag(); private: once_flag(const once_flag&); once_flag& operator=(const once_flag&); }; Best, Vicente

On Sunday 13 January 2013 16:08:28 Vicente J. Botet Escriba wrote:
C++11 defines the following interface. There is no STD_ONCE_INIT initializer.
struct once_flag {
constexpr once_flag() noexcept; once_flag(const once_flag&) = delete; once_flag& operator=(const once_flag&) = delete;
}; template<class Callable, class ...Args> void call_once(once_flag& flag, Callable func, Args&&... args);
Could this once_flag be implemented using the trick you used to mask the atomic dependency on the header file?
Hmm, I didn't know the standard defined call_once.
It is possible to define once_flag as defined by the standard.
Could you work on complementing your patch to take care of the C++11 interface?
Ok, but I'll have to modify the way BOOST_THREAD_PROVIDES_ONCE_CXX11 is defined to disable the C++11 interface when constexpr is not available.
But the previous implementation seem incorrect to me. once_flag *must* be either POD or have a constexpr default constructor while the previous code (as well as the code in win32/once.hpp) uses BOOST_CONSTEXPR which can be empty. BOOST_THREAD_PROVIDES_ONCE_CXX11 macro doesn't depend on constexpr or noexcept availability so it can't be used to enable C++11 implementation.
Andrey, could you explain me why once_flag must be either be a POD or follow strictly the C++11 interface? In other words, what are the problems defining it as follows?
struct once_flag { once_flag(); private: once_flag(const once_flag&); once_flag& operator=(const once_flag&); };
once_flag must always be statically initialized, that's the key feature that allows call_once to be thread-safe. In C++03 this can only be guaranteed for POD types. C++11 also includes constant initialization into static initialization (i.e. when objects are initialized with constexpr constructors). All other objects are initialized during dynamic initialization, which is not thread-safe.

Le 13/01/13 16:16, Andrey Semashev a écrit :
once_flag must always be statically initialized, that's the key feature that allows call_once to be thread-safe. In C++03 this can only be guaranteed for POD types. C++11 also includes constant initialization into static initialization (i.e. when objects are initialized with constexpr constructors). All other objects are initialized during dynamic initialization, which is not thread-safe.
Thanks for clarifications, I understand your concern. The non-POD once_flag implementation can not be used for static once_flag instances in a thread safe mode. Just for curiosity, from where comes the restriction that once_flag must always be statically initialized? Could you point me to the standard? Best, Vicente

On Sun, Jan 13, 2013 at 9:12 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Le 13/01/13 16:16, Andrey Semashev a écrit :
once_flag must always be statically initialized, that's the key feature that allows call_once to be thread-safe. In C++03 this can only be guaranteed for POD types. C++11 also includes constant initialization into static initialization (i.e. when objects are initialized with constexpr constructors). All other objects are initialized during dynamic initialization, which is not thread-safe.
Thanks for clarifications, I understand your concern. The non-POD once_flag implementation can not be used for static once_flag instances in a thread safe mode. Just for curiosity, from where comes the restriction that once_flag must always be statically initialized? Could you point me to the standard?
The standard only contains once_flag since C++11, and as you quoted its definition, it uses constexpr default constructor. The fact that it results in static initialization is outlined in 3.6.2/2. As for C++03, I cannot point you to a particular paper that defines once_flag; this is a matter of each particular implementation. However, it is known that dynamic initialization is just as unsafe in C++03 as it is in C++11, even more unsafe WRT function-local statics. In 3.6.2/1 in C++03 it is stated that "Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.", so one (if not the only one) way to implement once_flag safely is to make it a POD type. One thing still concerns me though with the C++11 code. The standard specifies that once_flag as non-copyable. This is quite reasonable when once_flag is used as described by the standard. OTOH, Boost.Thread supports the following code for backward compatibility: static once_flag flag = BOOST_ONCE_INIT; which expands to: static once_flag flag = once_flag(); Technically, this invokes the default constructor and then the copy constructor. Compilers may be smart enough to optimize the copy away, which explains why it compiles. But I'm not quite sure this is still a constant initialization, especially considering the bug in clang (it defines implicit constructor without constexpr). To be on the safe side I would probably define a special explicit initializing constructor marked with constexpr so that the initialization could look like this: static once_flag flag = {0};

Le 13/01/13 18:51, Andrey Semashev a écrit :
On Sun, Jan 13, 2013 at 9:12 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Le 13/01/13 16:16, Andrey Semashev a écrit :
once_flag must always be statically initialized, that's the key feature that allows call_once to be thread-safe. In C++03 this can only be guaranteed for POD types. C++11 also includes constant initialization into static initialization (i.e. when objects are initialized with constexpr constructors). All other objects are initialized during dynamic initialization, which is not thread-safe.
Thanks for clarifications, I understand your concern. The non-POD once_flag implementation can not be used for static once_flag instances in a thread safe mode. Just for curiosity, from where comes the restriction that once_flag must always be statically initialized? Could you point me to the standard? The standard only contains once_flag since C++11, and as you quoted its definition, it uses constexpr default constructor. The fact that it results in static initialization is outlined in 3.6.2/2.
As for C++03, I cannot point you to a particular paper that defines once_flag; this is a matter of each particular implementation. However, it is known that dynamic initialization is just as unsafe in C++03 as it is in C++11, even more unsafe WRT function-local statics. In 3.6.2/1 in C++03 it is stated that "Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.", so one (if not the only one) way to implement once_flag safely is to make it a POD type.
One thing still concerns me though with the C++11 code. The standard specifies that once_flag as non-copyable. This is quite reasonable when once_flag is used as described by the standard. OTOH, Boost.Thread supports the following code for backward compatibility:
static once_flag flag = BOOST_ONCE_INIT;
which expands to:
static once_flag flag = once_flag(); Technically, this invokes the default constructor and then the copy constructor. Compilers may be smart enough to optimize the copy away, which explains why it compiles. But I'm not quite sure this is still a constant initialization, especially considering the bug in clang (it defines implicit constructor without constexpr). To be on the safe side I would probably define a special explicit initializing constructor marked with constexpr so that the initialization could look like this:
static once_flag flag = {0};
Well, the definition removes the copy constructor and the assignment struct once_flag { BOOST_THREAD_NO_COPYABLE(once_flag) After adding static boost::once_flag once2 = BOOST_ONCE_INIT; to example/once.cpp and I'm seen the following kind of error on a lot of C++11 compilers darwin.compile.c++ ../../../bin.v2/libs/thread/test/ex_once2.test/darwin-4.7.0x/debug/threading-multi/once.o ../example/once.cpp:16:33: error: use of deleted function ‘boost::once_flag::once_flag(const boost::once_flag&)’ In file included from ../../../boost/thread/once.hpp:19:0, from ../example/once.cpp:10: ../../../boost/thread/pthread/once_atomic.hpp:37:5: error: declared here or clang-darwin.compile.c++ ../../../bin.v2/libs/thread/test/ex_once2_lib.test/clang-darwin-3.2xl/debug/threading-multi/once.o ../example/once.cpp:16:25: error: call to deleted constructor of 'boost::once_flag' static boost::once_flag once2 = BOOST_ONCE_INIT; ^ ~~~~~~~~~~~~~~~ ../../../boost/thread/pthread/once_atomic.hpp:37:30: note: function has been explicitly marked deleted here BOOST_THREAD_NO_COPYABLE(once_flag) ^ ../../../boost/thread/detail/delete.hpp:42:35: note: expanded from macro 'BOOST_THREAD_NO_COPYABLE' BOOST_THREAD_DELETE_COPY_CTOR(CLASS) \ ^ ../../../boost/thread/detail/delete.hpp:20:7: note: expanded from macro 'BOOST_THREAD_DELETE_COPY_CTOR' CLASS(CLASS const&) = delete; \ ^ Maybe it is worth to add a portable extension to the C++11 to ensure compatibility. BTW, couldn't the lock scope be reduced as follows BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { // ++++++++ pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); } // ++++++++ BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); } Best, Vicente

On Sun, Jan 13, 2013 at 10:32 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Well, the definition removes the copy constructor and the assignment
struct once_flag { BOOST_THREAD_NO_COPYABLE(once_flag)
After adding
static boost::once_flag once2 = BOOST_ONCE_INIT;
to example/once.cpp
and I'm seen the following kind of error on a lot of C++11 compilers
Does this mean that BOOST_ONCE_INIT is not supported and tested with C++11 compilers? Seems an oversight to me.
Maybe it is worth to add a portable extension to the C++11 to ensure compatibility.
This would be very useful, IMHO, since the same code could be used for both C++03 and C++11.
BTW, couldn't the lock scope be reduced as follows
BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { // ++++++++ pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); } // ++++++++ BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
}
No, this is not correct because this would lead to missed notifications. Condition variables should always be used under the locked mutex.

Le 13/01/13 19:41, Andrey Semashev a écrit :
On Sun, Jan 13, 2013 at 10:32 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Well, the definition removes the copy constructor and the assignment
struct once_flag { BOOST_THREAD_NO_COPYABLE(once_flag)
After adding
static boost::once_flag once2 = BOOST_ONCE_INIT;
to example/once.cpp
and I'm seen the following kind of error on a lot of C++11 compilers Does this mean that BOOST_ONCE_INIT is not supported and tested with C++11 compilers? Seems an oversight to me.
Maybe it is worth to add a portable extension to the C++11 to ensure compatibility. This would be very useful, IMHO, since the same code could be used for both C++03 and C++11.
BTW, couldn't the lock scope be reduced as follows
BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { // ++++++++ pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); } // ++++++++ BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
} No, this is not correct because this would lead to missed notifications. Condition variables should always be used under the locked mutex.
I believed it also but the mutex shouldn't be locked while doing notifications, only when you change the condition and when you wait for the condition to be notified. Best, Vicente

On Sun, Jan 13, 2013 at 11:15 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
I believed it also but the mutex shouldn't be locked while doing notifications, only when you change the condition and when you wait for the condition to be notified.
Looking at the code now it seems there is a potential race condition. The commit or rollback can set the flag and issue the broadcast just before the enter method acquires the lock and blocks in the cv. I suppose there should be a second check for the flag value under the lock in the enter_once_region method. The locks in commit and rollback functions can be removed altogether then.

On Sun, Jan 13, 2013 at 11:44 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Sun, Jan 13, 2013 at 11:15 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
I believed it also but the mutex shouldn't be locked while doing notifications, only when you change the condition and when you wait for the condition to be notified.
Looking at the code now it seems there is a potential race condition. The commit or rollback can set the flag and issue the broadcast just before the enter method acquires the lock and blocks in the cv.
I suppose there should be a second check for the flag value under the lock in the enter_once_region method. The locks in commit and rollback functions can be removed altogether then.
I attached the updated once.cpp.

On Mon, Jan 14, 2013 at 12:04 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Sun, Jan 13, 2013 at 11:44 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Sun, Jan 13, 2013 at 11:15 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
I believed it also but the mutex shouldn't be locked while doing notifications, only when you change the condition and when you wait for the condition to be notified.
Looking at the code now it seems there is a potential race condition. The commit or rollback can set the flag and issue the broadcast just before the enter method acquires the lock and blocks in the cv.
I suppose there should be a second check for the flag value under the lock in the enter_once_region method. The locks in commit and rollback functions can be removed altogether then.
I attached the updated once.cpp.
No, that code is not correct either. The lock in commit and rollback functions *is* needed and it *must* cover both the store and broadcast exactly to prevent missed notifications. Otherwise thread A can check for the flag value but not yet enter pthread_cond_wait and thread B can store the new flag value and issue pthread_cond_broadcast. I attached the corrected code. Apologies for the confusion.

Le 13/01/13 21:32, Andrey Semashev a écrit :
On Mon, Jan 14, 2013 at 12:04 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Sun, Jan 13, 2013 at 11:44 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Sun, Jan 13, 2013 at 11:15 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
I believed it also but the mutex shouldn't be locked while doing notifications, only when you change the condition and when you wait for the condition to be notified. Looking at the code now it seems there is a potential race condition. The commit or rollback can set the flag and issue the broadcast just before the enter method acquires the lock and blocks in the cv.
I suppose there should be a second check for the flag value under the lock in the enter_once_region method. The locks in commit and rollback functions can be removed altogether then. I attached the updated once.cpp. No, that code is not correct either. The lock in commit and rollback functions *is* needed and it *must* cover both the store and broadcast exactly to prevent missed notifications. Otherwise thread A can check for the flag value but not yet enter pthread_cond_wait and thread B can store the new flag value and issue pthread_cond_broadcast.
Wouldn't be useful to use double check here BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); if (f.load(memory_order_acquire) != initialized) { pthread::pthread_mutex_scoped_lock lk(&once_mutex); if (f.load(memory_order_acquire) != initialized) { while (true) { atomic_int_type expected = uninitialized; if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire)) { // We have set the flag to in_progress return true; } else if (expected == initialized) { // Another thread managed to complete the initialization return false; } else { // Wait until the initialization is complete BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex)); } } } } return false; } BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); } BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); } BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(uninitialized, memory_order_release); } BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); } Vicente

On Mon, Jan 14, 2013 at 2:24 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Le 13/01/13 21:32, Andrey Semashev a écrit :
No, that code is not correct either. The lock in commit and rollback functions *is* needed and it *must* cover both the store and broadcast exactly to prevent missed notifications. Otherwise thread A can check for the flag value but not yet enter pthread_cond_wait and thread B can store the new flag value and issue pthread_cond_broadcast.
Wouldn't be useful to use double check here
BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT
{ atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); if (f.load(memory_order_acquire) != initialized) { pthread::pthread_mutex_scoped_lock lk(&once_mutex); if (f.load(memory_order_acquire) != initialized) { while (true) { atomic_int_type expected = uninitialized; if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire)) { // We have set the flag to in_progress return true; } else if (expected == initialized) { // Another thread managed to complete the initialization return false; } else { // **** // Wait until the initialization is complete BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex)); } } } } return false;
}
BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT { atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(initialized, memory_order_release); } BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); }
BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT
{ atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); { pthread::pthread_mutex_scoped_lock lk(&once_mutex); f.store(uninitialized, memory_order_release); } BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
}
1. You have the race condition I described in my previous post. The broadcast is missed if it happens when the other thread is executing at **** position. 2. If you use the double-check pattern with the lock, you don't need to CAS the flag. 3. I think your code will have worse performance because you lock the mutex whenever you are about to enter the execution block while mine code will only lock in case of contention. This difference is not in the most performance critical path but it still exists. However, I didn't perform any tests to confirm that.

Le 14/01/13 04:23, Andrey Semashev a écrit : > On Mon, Jan 14, 2013 at 2:24 AM, Vicente J. Botet Escriba > <vicente.botet@wanadoo.fr> wrote: >> Le 13/01/13 21:32, Andrey Semashev a écrit : >> >>> No, that code is not correct either. The lock in commit and rollback >>> functions *is* needed and it *must* cover both the store and broadcast >>> exactly to prevent missed notifications. Otherwise thread A can check >>> for the flag value but not yet enter pthread_cond_wait and thread B >>> can store the new flag value and issue pthread_cond_broadcast. >>> >>> >> Wouldn't be useful to use double check here >> >> BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT >> >> { >> atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); >> if (f.load(memory_order_acquire) != initialized) >> { >> pthread::pthread_mutex_scoped_lock lk(&once_mutex); >> if (f.load(memory_order_acquire) != initialized) >> { >> while (true) >> { >> atomic_int_type expected = uninitialized; >> if (f.compare_exchange_strong(expected, in_progress, >> memory_order_acq_rel, memory_order_acquire)) >> { >> // We have set the flag to in_progress >> return true; >> } >> else if (expected == initialized) >> { >> // Another thread managed to complete the initialization >> return false; >> } >> else >> { > // **** >> // Wait until the initialization is complete >> BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex)); >> } >> } >> } >> } >> return false; >> >> } >> >> BOOST_THREAD_DECL void commit_once_region(once_flag& flag) >> BOOST_NOEXCEPT >> { >> atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); >> { >> pthread::pthread_mutex_scoped_lock lk(&once_mutex); >> f.store(initialized, memory_order_release); >> } // C*** >> BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); >> } >> >> BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) >> BOOST_NOEXCEPT >> >> { >> atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage); >> { >> pthread::pthread_mutex_scoped_lock lk(&once_mutex); >> f.store(uninitialized, memory_order_release); >> } // R*** >> BOOST_VERIFY(!pthread_cond_broadcast(&once_cv)); >> >> } > 1. You have the race condition I described in my previous post. The > broadcast is missed if it happens when the other thread is executing > at **** position. I don't think so. The thread B has notified a commit in C*** or rollback in R***, the thread A could not be in //**** as the access to the flag is protected with the lock. Here follows the algorithm in libc++. You could see that the lock is released before notifying. void __call_once(volatile unsigned long& flag, void* arg, void(*func)(void*)) { pthread_mutex_lock(&mut); while (flag == 1) pthread_cond_wait(&cv, &mut); if (flag == 0) { #ifndef _LIBCPP_NO_EXCEPTIONS try { #endif // _LIBCPP_NO_EXCEPTIONS flag = 1; pthread_mutex_unlock(&mut); func(arg); pthread_mutex_lock(&mut); flag = ~0ul; pthread_mutex_unlock(&mut); pthread_cond_broadcast(&cv); #ifndef _LIBCPP_NO_EXCEPTIONS } catch (...) { pthread_mutex_lock(&mut); flag = 0ul; pthread_mutex_unlock(&mut); pthread_cond_broadcast(&cv); throw; } #endif // _LIBCPP_NO_EXCEPTIONS } else pthread_mutex_unlock(&mut); } > 2. If you use the double-check pattern with the lock, you don't need > to CAS the flag. Well this was the issue with the Trac ticket. The first check works only if T is such that the load/save is atomic. Maybe the access to the atomic flag inside the lock protected part could be relaxed I don't know all the possibilities of Boost.Atomic. > 3. I think your code will have worse performance because you lock the > mutex whenever you are about to enter the execution block while mine > code will only lock in case of contention. This difference is not in > the most performance critical path but it still exists. However, I > didn't perform any tests to confirm that. > I agree. But the condition variable needs that the code changing/getting the protect data needs to be protected with the same mutex that is used on the wait. IMHO, your previous implementation failed to do that as every access to the once flag in enter_once_region was not protected. Maybe Howard or Anthony (or someone else of course) could help us to determine the correctness of both implementations. Best, Vicente

On Mon, Jan 14, 2013 at 11:24 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
Le 14/01/13 04:23, Andrey Semashev a écrit :
1. You have the race condition I described in my previous post. The broadcast is missed if it happens when the other thread is executing at **** position.
I don't think so. The thread B has notified a commit in C*** or rollback in R***, the thread A could not be in //**** as the access to the flag is protected with the lock.
Ok, I'll leave this to you although I'm still not quite sure this is safe. I'll have to examine the algorithm closer later to resolve all my doubts.
2. If you use the double-check pattern with the lock, you don't need to CAS the flag.
Well this was the issue with the Trac ticket. The first check works only if T is such that the load/save is atomic. Maybe the access to the atomic flag inside the lock protected part could be relaxed I don't know all the possibilities of Boost.Atomic.
All loads and stores of atomic<> are atomic. The problem of split loads and stores should never occur when atomic<> is used, otherwise there is a severe bug in Boost.Atomic. Using CAS instead of a load/store does not make this guarantee any stronger. Note that this guarantee does not depend on whether the operation is done under lock or not.
3. I think your code will have worse performance because you lock the mutex whenever you are about to enter the execution block while mine code will only lock in case of contention. This difference is not in the most performance critical path but it still exists. However, I didn't perform any tests to confirm that.
I agree. But the condition variable needs that the code changing/getting the protect data needs to be protected with the same mutex that is used on the wait.
Yes, but not necessarily every modification of the control flag has to be protected.
IMHO, your previous implementation failed to do that as every access to the once flag in enter_once_region was not protected.
Yes, that was a valid optimization. The flag is checked the second time under the lock just before entering the wait on the condition variable. This keeps the algorithm correct while allowing to remove the lock from the fast path.

On Sunday 13 January 2013 16:08:28 Vicente J. Botet Escriba wrote:
Could you work on complementing your patch to take care of the C++11 interface?
I have updated the patch in the ticket #5752 with the C++11 definition of atomic_flag. Note that the new patch disables the BOOST_THREAD_PROVIDES_ONCE_CXX11 macro when constexpr is not available. The patch also adds some heuristic code for detecting an atomic-compatible integer type to even better avoid using the spinlock pool of Boost.Atomic. The modified files are attached as well. These are boost/thread/pthread/once.hpp, boost/thread/detail/config.hpp and libs/thread/src/pthread/once.cpp.

Le 13/01/13 17:15, Andrey Semashev a écrit :
On Sunday 13 January 2013 16:08:28 Vicente J. Botet Escriba wrote:
Could you work on complementing your patch to take care of the C++11 interface? I have updated the patch in the ticket #5752 with the C++11 definition of atomic_flag. Note that the new patch disables the BOOST_THREAD_PROVIDES_ONCE_CXX11 macro when constexpr is not available. It is OK. I will update the documentation to clarify this point. The patch also adds some heuristic code for detecting an atomic-compatible integer type to even better avoid using the spinlock pool of Boost.Atomic. Great. Does it mean that Boost.Thread must link with Boost.Atomic when when the spinlock pool is needed? Is there a know platform/compiler for which the condition
BOOST_ATOMIC_INT_LOCK_FREE == 2 || BOOST_ATOMIC_SHORT_LOCK_FREE == 2 || BOOST_ATOMIC_CHAR_LOCK_FREE == 2 || BOOST_ATOMIC_LONG_LOCK_FREE == 2 || (defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2) is false? I have included your implementation conditionally in separated files once_atomic.hpp and once_atomic.cpp. I will commit it this evening after I run my regression tests and I'll activate your implementation in order to see how the Boost regression tests behave. I hope that everything will be OK. Thanks for your contribution. Vicente

On Sun, Jan 13, 2013 at 10:04 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
The patch also adds some heuristic code for detecting an atomic-compatible integer type to even better avoid using the spinlock pool of Boost.Atomic.
Great. Does it mean that Boost.Thread must link with Boost.Atomic when when the spinlock pool is needed?
Yes, Boost.Thread has to link with Boost.Atomic if the spinlock pool is used. You can always link and hope the linker eliminates the dependency when not needed.
Is there a know platform/compiler for which the condition
BOOST_ATOMIC_INT_LOCK_FREE == 2 || BOOST_ATOMIC_SHORT_LOCK_FREE == 2 || BOOST_ATOMIC_CHAR_LOCK_FREE == 2 || BOOST_ATOMIC_LONG_LOCK_FREE == 2 || (defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2)
is false?
Any platform not supported by Boost.Atomic. The condition will be positive on any platform currently supported by Boost.Atomic.
I have included your implementation conditionally in separated files once_atomic.hpp and once_atomic.cpp. I will commit it this evening after I run my regression tests and I'll activate your implementation in order to see how the Boost regression tests behave. I hope that everything will be OK.
Great, thank you.

Le 13/01/13 19:15, Andrey Semashev a écrit :
On Sun, Jan 13, 2013 at 10:04 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
The patch also adds some heuristic code for detecting an atomic-compatible integer type to even better avoid using the spinlock pool of Boost.Atomic. Great. Does it mean that Boost.Thread must link with Boost.Atomic when when the spinlock pool is needed? Yes, Boost.Thread has to link with Boost.Atomic if the spinlock pool is used. You can always link and hope the linker eliminates the dependency when not needed.
Is there a know platform/compiler for which the condition
BOOST_ATOMIC_INT_LOCK_FREE == 2 || BOOST_ATOMIC_SHORT_LOCK_FREE == 2 || BOOST_ATOMIC_CHAR_LOCK_FREE == 2 || BOOST_ATOMIC_LONG_LOCK_FREE == 2 || (defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2)
is false? Any platform not supported by Boost.Atomic. The condition will be positive on any platform currently supported by Boost.Atomic.
I would like to have an alternative for these platforms to avoid making Boost.Thread fail just because Boost.Atomic is not supported. Best, Vicente

On Sun, Jan 13, 2013 at 10:43 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
I would like to have an alternative for these platforms to avoid making Boost.Thread fail just because Boost.Atomic is not supported.
It won't fail, it will use the spinlock pool. When the spinlock cannot be implemented with atomic ops (i.e. on a not supported platform) the spinlock is implemented as boost::mutex from Boost.Thread. I realize this would make the implementation suboptimal in case of call_once. In that case we could fallback to a special implementation that only uses pthread API. That would require to always lock the mutex, even when the initialization is already done. PS: It might be worthwhile to actually try to use the spinlocks from pthread API in Boost.Atomic.

On January 12, 2013 9:57:22 PM "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
What I was asking for is just a patch to ensure that
typedef unsigned long uintmax_atomic_t;
is atomic in all platforms.
One more note. Is your idea to have a typedef for an integer type that is guaranteed to be supported by atomic ops across all platforms? If so, I don't think this is feasible. Most architectures support atomic ops on 32 bit operands but that is still not a general solution.

Le 12/01/13 18:29, Andrey Semashev a écrit :
On Saturday 12 January 2013 17:49:38 Vicente J. Botet Escriba wrote:
Le 12/01/13 17:09, Andrey Semashev a écrit :
On Saturday 12 January 2013 15:48:21 Vicente J. Botet Escriba wrote:
On Saturday 12 January 2013 12:08:10 Vicente J. Botet Escriba wrote:
Le 12/01/13 11:51, Andrey Semashev a écrit : > Anyway, can Boost.Thread be modified in such a way so that > Boost.Atomic > use is not exposed to the user? E.g. so that call_once invokes a > compiled > function implemented within Boost.Thread library that uses > Boost.Atomic > to modify the once flag. yes, this will be great. I don't know Boost.Atomic details to try to do this. Andrey do you mind to provide a patch that doesn't needs to link with boost_atomic? I attached the patch (for posix only). It appeared a bit hacky and I'm not sure if you're ok with it. Thanks for the quick patch. Shouldn't the patch concern only boost/thread/pthread/once.hpp and
Le 12/01/13 14:26, Andrey Semashev a écrit : libs/thread/src/pthread/once.hpp? Could you send the resulting files also? There is no libs/thread/src/pthread/once.hpp file as far as I can see. RIght. I meant
libs/thread/src/pthread/once.cpp It is patched.
The boost/thread/pthread/once.hpp file is no longer needed and can be removed (as well as boost/thread/win32/once.hpp when win32 version is implemented). The complete public code is platform-independent and resides in boost/thread/once.hpp after the patch is applied. IIRC the problem was on the Posix implementation, so a specific patch for the pthread files will be desirable. Ok, I intended to port both Windows and POSIX implementations but we could do it just for POSIX variant.
I attached the updated patch to the ticket. The Jamfile will also have to be updated to add the dependency on Boost.Atomic. However, the Jamfile in Boost.Thread is rather complicated so I didn't do that.
Anyway, could you send the resulting files to make easier the review? The files are attached. These should be boost/thread/posix/once.hpp and libs/thread/src/posix/once.cpp, other files are intact.
Hi, I have studied your patch and I like how you have separated the test on whether the function has been called, the commit and the rollback. I believe I will adopt this separation. I have studied also the current algorithm implementation and the algorithm on which it was based on [1]. The efficiency of the algorithm in [1], depends on, I quote " [thread_local] There is a way to access thread-specific data or thread-local storage. The technique is useful only if such an access is faster than a memory barrier. For the disassembly above, the compiler used its ability to access thread-local storage using variables declared with __thread, so the thread-local access is a normal "mov" instruction with a different segment register. [atomicity] There exists an integral type T (such as sig_atomic_t) such that loads and stores of a variable of type T are atomic. It is not possible to read from such a variable any value that was not written to the variable, even if multiple reads and writes occur on many processors. Another way to say this is that variables of type T do not exhibit word tearing when accessed with full-width accesses. [*once_bound] The number of pthread_once_t variables in a programme is bounded and can be counted in an instance of type T without overflow. If T is 32-bits, this implementation requires that the number of pthread_once_t variables in a programme not exceed 2**31-3. (Recall that pthread_once_t variables are intended to have static storage class, so it would be remarkable for such a huge number to exist.) [monotonicity] Accesses to a single variable V of type T by a single thread X appear to occur in programme order. For example, if V is initially 0, then X writes 1, and then 2 to V, no thread (including but not limited to X) can read a value from V and then subsequently read a lower value from V. (Notice that this does not prevent arbitrary load and store reordering; it constrains ordering only between actions on a single memory location. This assumption is reasonable on all architectures that I currently know about. I suspect that the Java and CLR memory models require this assumption also.) " The current implementation uses pthread_getspecific/pthread_setspecific which I'm not sure "is faster than a memory barrier". The type T is unsigned long which doesn't satisfy [atomicity] and [monotonicity] requirements on all platforms. The Boost.Thread should be changed so that the current algorithm is used only when the requirements are meet (this will mean at least to change the implementation of the thread specific data and that the datatype is adapted to the platform). So, to be portable we need an alternative algorithm, which could be the one you (Andrey) proposed in the patch. As the patch uses Boost.Atomic, this alternative will be really efficient only when atomic<uchar_t> is lock free. libc++ implements it using an algorithm that is close to the double checked locking. It reuse the mutex needed to wait on the condition variable to protect the whole data and in addition making a non protected test on the flag state (see ********) template<class _Callable> void call_once(once_flag& __flag, _Callable __func) { if (__flag.__state_ != ~0ul) // ********** { __call_once_param<_Callable> __p(__func); __call_once(__flag.__state_, &__p, &__call_once_proxy<_Callable>); // Here the flag is tested again with the mutex locked. } } This algorithm is efficient but I suspect that this algorithm is not always correct for the same reasons the Boost.Thread current algorithm is not. But on which platforms the libc++ implementation is correct? Howard, could you comment if you are reading this post. For the time been I will play with the Andrey, the Howard (libc++) implementation and update the current implementation for the thread specific data. I will add some compiler flags that will direct to one of these implementations and make the best choice depending on the platform/compiler. Many thank Andrey, Vicente [1] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html#Appendix). [2] http://llvm.org/svn/llvm-project/libcxx/trunk/include/mutex and http://llvm.org/svn/llvm-project/libcxx/trunk/src/mutex.cpp P.S. It is surprising that gcc implementation in libstdc++v3 needs to initialize a lot of data before using __gthread_once and seems to don't manage the case when the user function throw an exception. extern "C" { void __once_proxy() { #ifndef _GLIBCXX_HAVE_TLS function<void()> __once_call = std::move(__once_functor); if (unique_lock<mutex>* __lock = __get_once_functor_lock_ptr()) { // caller is using new ABI and provided lock ptr __get_once_functor_lock_ptr() = 0; __lock->unlock(); } else __get_once_functor_lock().unlock(); // global lock #endif __once_call(); } } /// call_once template<typename _Callable, typename... _Args> void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) { #ifdef _GLIBCXX_HAVE_TLS auto __bound_functor = std::__bind_simple(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); __once_callable = &__bound_functor; __once_call = &__once_call_impl<decltype(__bound_functor)>; #else unique_lock<mutex> __functor_lock(__get_once_mutex()); auto __callable = std::__bind_simple(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); __once_functor = [&]() { __callable(); }; __set_once_functor_lock_ptr(&__functor_lock); #endif int __e = __gthread_once(&(__once._M_once), &__once_proxy); #ifndef _GLIBCXX_HAVE_TLS if (__functor_lock) __set_once_functor_lock_ptr(0); #endif if (__e) __throw_system_error(__e); }

On Jan 13, 2013, at 9:57 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
This algorithm is efficient but I suspect that this algorithm is not always correct for the same reasons the Boost.Thread current algorithm is not. But on which platforms the libc++ implementation is correct? Howard, could you comment if you are reading this post.
Yes, libc++ is currently using double-checked locking for call_once. The rationale is two-fold: 1. It is actually correct for those platforms I need to target today. 2. A correct implementation using atomic_load with memory_order_acquire wasn't available when I wrote call_once. <atomic> appeared in libc++ about 2 years later. It needed compiler support which was slow in coming. There are a couple of places in libc++ that need to be retrofitted to use <atomic>. In the cases where that retrofit is in a header (i.e. call_once), the retrofit will currently need to be protected by __has_feature(cxx_atomic) as clang shuts this feature off in C++03 mode. Howard

Le 13/01/13 18:38, Howard Hinnant a écrit :
On Jan 13, 2013, at 9:57 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
This algorithm is efficient but I suspect that this algorithm is not always correct for the same reasons the Boost.Thread current algorithm is not. But on which platforms the libc++ implementation is correct? Howard, could you comment if you are reading this post. Yes, libc++ is currently using double-checked locking for call_once. The rationale is two-fold:
1. It is actually correct for those platforms I need to target today. 2. A correct implementation using atomic_load with memory_order_acquire wasn't available when I wrote call_once. <atomic> appeared in libc++ about 2 years later. It needed compiler support which was slow in coming.
There are a couple of places in libc++ that need to be retrofitted to use <atomic>. In the cases where that retrofit is in a header (i.e. call_once), the retrofit will currently need to be protected by __has_feature(cxx_atomic) as clang shuts this feature off in C++03 mode.
Thanks Howard for confirming my suspicion. Best, Vicente

Howard Hinnant wrote:
There are a couple of places in libc++ that need to be retrofitted to use <atomic>. In the cases where that retrofit is in a header (i.e. call_once), the retrofit will currently need to be protected by __has_feature(cxx_atomic) as clang shuts this feature off in C++03 mode.
Not very on-topic, but... what does it mean that clang shuts the feature off? I see in <atomic> that you issue an #error when !__has_feature(cxx_atomic), but what would happen if this is removed? Don't the C11 intrinsics work regardless?

On Jan 14, 2013, at 1:11 PM, "Peter Dimov" <lists@pdimov.com> wrote:
Howard Hinnant wrote:
There are a couple of places in libc++ that need to be retrofitted to use <atomic>. In the cases where that retrofit is in a header (i.e. call_once), the retrofit will currently need to be protected by __has_feature(cxx_atomic) as clang shuts this feature off in C++03 mode.
Not very on-topic, but... what does it mean that clang shuts the feature off? I see in <atomic> that you issue an #error when !__has_feature(cxx_atomic), but what would happen if this is removed? Don't the C11 intrinsics work regardless?
I have no idea. I just checked the clang page and cxx_atomic appears to be undocumented. There is also a documented __has_feature(c_atomic). I just ran this test in C++03 mode: #include <iostream> int main() { std::cout << __c11_atomic_is_lock_free(1) << '\n'; std::cout << __has_feature(c_atomic) << '\n'; } It outputs: 1 0 I'm not sure what to make of that, or how much it can be trusted. Howard

Howard Hinnant wrote:
I just ran this test in C++03 mode:
#include <iostream>
int main() { std::cout << __c11_atomic_is_lock_free(1) << '\n'; std::cout << __has_feature(c_atomic) << '\n'; }
It outputs:
1 0
What does __has_extension(c_atomic) say?

On Jan 14, 2013, at 3:11 PM, "Peter Dimov" <lists@pdimov.com> wrote:
Howard Hinnant wrote:
I just ran this test in C++03 mode: #include <iostream> int main() { std::cout << __c11_atomic_is_lock_free(1) << '\n'; std::cout << __has_feature(c_atomic) << '\n'; } It outputs: 1 0
What does __has_extension(c_atomic) say?
1 0 1 Not a bad idea. :-) Howard

On 1/12/2013 5:37 AM, Vicente J. Botet Escriba wrote:
Hi,
in order to fix this issue, https://svn.boost.org/trac/boost/ticket/5752 : "boost::call_once() is unreliable on some platforms", I would like to use Boost.Atomic as suggested on the ticket. As Boost.Atomic is not a header-only library the user would need to link with boost_atomic.
As long as I can link against a static lib, I've got no problem.
Is this an acceptable change? I'm requesting this as recently there were some people reactive to some changes in Boost.Thread. Would this be considered an compatibility issue?
To me it is acceptable. I don't look at it as being a compatibility issue, assuming no semantic differences, other than fixing the reliability of call_once.
If yes should the fix for the call_once issue be included if the user request it using conditional compilation or even go on a separated version of Boost.Thread?
If a single implementation works, I would avoid the complexity of conditional compilation, or splitting a new lib. Jeff

Le 13/01/13 19:44, Jeff Flinn a écrit :
On 1/12/2013 5:37 AM, Vicente J. Botet Escriba wrote:
Hi,
in order to fix this issue, https://svn.boost.org/trac/boost/ticket/5752 : "boost::call_once() is unreliable on some platforms", I would like to use Boost.Atomic as suggested on the ticket. As Boost.Atomic is not a header-only library the user would need to link with boost_atomic.
As long as I can link against a static lib, I've got no problem.
Is this an acceptable change? I'm requesting this as recently there were some people reactive to some changes in Boost.Thread. Would this be considered an compatibility issue?
To me it is acceptable. I don't look at it as being a compatibility issue, assuming no semantic differences, other than fixing the reliability of call_once.
If yes should the fix for the call_once issue be included if the user request it using conditional compilation or even go on a separated version of Boost.Thread?
If a single implementation works, I would avoid the complexity of conditional compilation, or splitting a new lib.
Thanks Jeff. I take your answer as no problem even if linking with Boost.Atomic is needed on some platforms. Thanks, Vicente
participants (6)
-
Andrey Semashev
-
Howard Hinnant
-
Jeff Flinn
-
Peter Dimov
-
Tim Blechmann
-
Vicente J. Botet Escriba