I am certain that, in practice, use of ::new is thread-safe. My question is what part of the standard provides that guarantee, if any? Is this a convention? Is this something where the standard gives implementations a lot of latitude (like the relatively loose constraints about what size each data type is) to support a wide variety of hardware?
I'm hoping that there's just a line in the C++11 standard somewhere that explicitly specifies "implementations of ::new must be thread-safe".
I'd also love to see some standardese about the thread-safety of operator new overloads. I imagine that they would also need to be required to be thread-safe, but these functions also do not fall under the blanket guarantee that const => thread safe (in C++11).
Thanks!
I believe this is implicitly guaranteed by the C++11 standard. If it were not, then usage of the operator new or new expression might cause a data race, and that would not be allowed by the standard. For reference, see §17.6.5.9 Data race avoidance and also
18.6.1.4 Data races [new.delete.dataraces]
"The library versions of operator new and operator delete, user replacement versions of global operator new and operator delete, and the C standard library functions calloc, malloc, realloc, and free shall not introduce data races (1.10) as a result of concurrent calls from different threads. Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order."
Your own overrides or your own replacements for the global operators should fulfill this requirement as well.
See also this proposal N3664 "Clarifying Memory Allocation", which puts more emphasis on that matter.
The C++ standard does not outright require that new be thread-safe. Some implementations explicitly support building C++ code in single-threaded mode, where the C standard library, including malloc() may not be thread-safe. The platforms most of us use every day do offer thread-safe allocation, of course.
Even if your platform provides a thread-safe new, you still need to be wary if you use any libraries which implement their own operator new, or if you do so yourself. It's certainly possible to write a new which works only in a single thread--maybe even intentionally!
Related
A quick thought experiment before getting to the question. Imagine someone is implementing std::malloc (say, one of the JEMalloc or TCMalloc folks). One of the very basic things they would need is the ability to know that the program will not call back into malloc once execution has entered the implementation of std::malloc.
For example,
void* malloc(...) {
auto lck = std::unique_lock{malloc_mutex};
// .. memory allocation business logic
}
Now if there is a signal in between the lock and the business logic for the allocation, we can deadlock if the signal handler calls back into std::malloc. It is not designed to be re-entrant, the C++ standard requires that a signal handler registered with std::signal does not call back into operator new (which can possibly call back into malloc, therefore it is required that a user-defined signal handler not call back into malloc if it is to be considered portable across all implementations of the language).
§[support.signal]p3 in the most recent version of the standard outlines this requirement
An evaluation is signal-safe unless it includes one of the following:
a call to any standard library function, except for plain lock-free atomic operations and functions explicitly identified as signal-safe. [ Note: This implicitly excludes the use of new and delete expressions that rely on a library-provided memory allocator. — end note ]
However, the C++ standard seemingly says nothing about how function stacks are to be implemented for threads of execution (see this question: C++ threads stack address range), this means that a function dispatch within std::malloc's implementation might call into operator new if the program is compiled with segmented stacks.
How can one possibly implement a function like std::malloc in that case? If indeed, the C++ standard offers no such guarantees, then what does? How can we know that the implementation of a regular function goes through the regular stack allocation process (stack pointer increment)? Which standard (eg. ABI, compiler, POSIX) covers this?
The implementation is required to use a signal-safe allocator for its stack frames. This follows from the fact that function calls (to non-library functions) in signal handlers are permitted. The implementation can use malloc or operator new, but only if those allocators are themselves signal-safe.
Under the logic of the C++ Standard, the implementation is considered as a whole. In particular, any part of an implementation may assume anything about any other part of the implementation.
That means for this question that std::malloc and the signal handler may assume things about each other. Some implementations may decide that their std::malloc implementation is async-safe, others may decide it's not. But there are a myriad of other assumptions that might exist - alignment, contiguity, recycling of free'd addresses, etc. Since this is all internal to implementations, there's no Standard describing this.
That is a problem for "replacement mallocs". You can implement JE::malloc but std:: is special. C++ at least acknowledged the possibility of a replacement operator new but even that was never specified to this detailed level.
There are contexts in which we want our C++ code to not perform dynamic memory allocation ('on the heap'), specifically in some embedded development use cases.
There are standard library classes which can be implemented without dynamic memory allocation: optional, array, tuple, variant to name a few.
The same is true for standard library free functions.
Are there any such classes or functions which are guaranteed by the standard to not allocate memory dynamically? The only functions I could find with such a guarantee are the placement new() functions.
There are very few cases if any where the C++ standard makes any direct guarantee about not using dynamic memory.
On systems where dynamic memory allocation is signal-unsafe, you can be certain that all functions listed as signal-safe are non-allocating. The standard mentions
_Exit
abort
forward
initializer_list functions
memcpy
memmove
move
move_if_noexcept
numeric_limits members
quick_exit
signal
type traits
plain lock-free atomic operations
If you can assume conformance to another standard, POSIX, then it lists more functions that are async-signal-safe. Some of these functions listed by POSIX are provided by C++ (and C) standards as well (such as strcat), and therefore those standard C++ functions will be signal safe on all POSIX systems.
There are a few functions in [new.delete.placement], which are non-allocating by definition.
Another question separate from guarantees is, whether a reasonable implementation of a function or a type would not allocate. Many, many things such as std::tuple and std::array (with non-allocating type arguments naturally) fall into this category.
It would be reasonable that functions which are declared noexcept, and do not have any failure path (like setting error code, returning error indicating value, or terminating the process) shouldn't allocate, since allocation may throw.
Conversely, there are functions that in a reasonable implementation do allocate dynamic memory. Obviously those that involve allocators, as well as those listed in the SO post that you linked. One non-obvious one that often bites people writing signal handlers is missing from the list: It is not at all reasonable to expect printf or any of its related functions to not allocate.
I know it is not safe for malloc or free to be called, directly or indirectly, from a signal handler.
But, if I can guarantee that at least one shared reference will remain alive, is it then safe to copy-construct and destruct additional shared or weak references, or do I have to roll my own refcounting?
(Yes, I know signal handlers usually shouldn't do much. But I have a good reason this time.)
The C++ standard defines the concept of a "plain old function" as follows:
A POF (“plain old function”) is a function that uses only features from [the C/C++] common subset, and that does not directly or indirectly use any function that is not a POF, except that it may use plain lock-free atomic operations.
Furthermore:
The behavior of any function other than a POF used as a signal handler in a C++ program is implementation-defined.
Obviously, C++ class objects are not part of the C/C++ common subset, and therefore using them in a signal handler yields implementation-defined behavior.
Now, many implementations will allow you limited usage of C++ features. If your implementation doesn't permit memory allocations or exceptions, then here's what we know about those smart pointer types.
Then all weak_ptr constructors are explicitly declared noexcept. So they can't throw exceptions. This also means that they're not allowed to allocate memory (since failing to allocate memory would throw an exception). Yes, they could allocate memory and std::terminate if it fails, but that would be exceedingly rude for an implementation to do.
The copy&move constructors of shared_ptr similarly is noexcept, so the same applies. This is also true for the aliasing constructor for shared_ptr.
If you are absolutely certain that at least one shared_ptr will still exist, then destroying a shared_ptr is explicitly stated to not have side effects. Which probably includes memory de-allocations.
Those are the guarantees that the standard library gives you.
I am certain that, in practice, use of ::new is thread-safe. My question is what part of the standard provides that guarantee, if any? Is this a convention? Is this something where the standard gives implementations a lot of latitude (like the relatively loose constraints about what size each data type is) to support a wide variety of hardware?
I'm hoping that there's just a line in the C++11 standard somewhere that explicitly specifies "implementations of ::new must be thread-safe".
I'd also love to see some standardese about the thread-safety of operator new overloads. I imagine that they would also need to be required to be thread-safe, but these functions also do not fall under the blanket guarantee that const => thread safe (in C++11).
Thanks!
I believe this is implicitly guaranteed by the C++11 standard. If it were not, then usage of the operator new or new expression might cause a data race, and that would not be allowed by the standard. For reference, see §17.6.5.9 Data race avoidance and also
18.6.1.4 Data races [new.delete.dataraces]
"The library versions of operator new and operator delete, user replacement versions of global operator new and operator delete, and the C standard library functions calloc, malloc, realloc, and free shall not introduce data races (1.10) as a result of concurrent calls from different threads. Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order."
Your own overrides or your own replacements for the global operators should fulfill this requirement as well.
See also this proposal N3664 "Clarifying Memory Allocation", which puts more emphasis on that matter.
The C++ standard does not outright require that new be thread-safe. Some implementations explicitly support building C++ code in single-threaded mode, where the C standard library, including malloc() may not be thread-safe. The platforms most of us use every day do offer thread-safe allocation, of course.
Even if your platform provides a thread-safe new, you still need to be wary if you use any libraries which implement their own operator new, or if you do so yourself. It's certainly possible to write a new which works only in a single thread--maybe even intentionally!
I'm aware of the fact that the containers from standard library are not thread-safe. By that I used to think that a container, say of type std::list, cannot be accessed by more than one thread concurrently (some of which may modify the container). But now it seems that there is more to it than meets the eye; something more subtle, something not so obvious, well at least to me.
For example, consider this function which accepts the first argument by value:
void log(std::string msg, severity s, /*...*/)
{
return; //no code!
}
Is this thread-safe?
At first, it seems that it is thread-safe, as the function body accesses no shared modifiable resources, hence thread-safe. On second thought, it comes to me that when invoking such a function, an object of type std::string will be created, which is the first argument, and I think that construction of this object isn't thread-safe, as it internally uses std::allocator, which I believe isn't thread-safe. Hence invoking such a function isn't thread-safe either. But if it is correct, then what about this:
void f()
{
std::string msg = "message"; //is it thread-safe? it doesn't seem so!
}
Am I going right? Can we use std::string (or any container which uses std::allocator internally) in multi-threaded program?
I'm specifically talking about containers as local variables, as opposed to shared objects.
I searched google and found many similar doubts, with no concrete answer. I face similar problem as his:
c++ allocators thread-safe?
Please consider C++03 and C++11, both.
In C++11, std::allocator is thread safe. From its definition:
20.6.9.1/6: Remark: the storage is obtained by calling ::operator new(std::size_t)
and from the definition of ::operator new:
18.6.1.4: The library versions of operator new and operator delete, user replacement versions of global operator new and operator delete, and the C standard library functions calloc, malloc, realloc, and free shall
not introduce data races (1.10) as a result of concurrent calls from different threads.
C++03 had no concept of threads, so any thread safety was implementation-specific; you'd have to refer to your implementation's documentation to see what guarantees it offered, if any. Since you're using Microsoft's implementation, this page says that it is safe to write to multiple container objects of the same class from many threads, which implies that std::allocator is thread-safe.
In C++11 this would be addressed for the default allocator in:
20.6.9.1 allocator members [allocator.members]
Except for the destructor, member functions of the default allocator
shall not introduce data races (1.10) as a result of concurrent calls
to those member functions from different threads. Calls to these
functions that allocate or deallocate a particular unit of storage
shall occur in a single total order, and each such deallocation call
shall happen before the next allocation (if any) in this order.
Any user-provided allocator would have to hold to the same constraints if it were going to be used across different threads.
Of course, for earlier versions of the standard, nothing is said about this since they didn't talk about multithreading. If an implementation were to support multithreading (as many or most do), it would be responsible for taking care of those issues. Similar to the way implementations provide a thread-safe malloc() (and other library functions) for C and C++ even though the standards prior to very recently said nothing about that.
As you may have already figured, there is not going to be an easy yes or no answer. However, I think this may help:
http://www.cs.huji.ac.il/~etsman/Docs/gcc-3.4-base/libstdc++/html/faq/index.html#5_6
I quote verbatim:
5.6 Is libstdc++-v3 thread-safe?
libstdc++-v3 strives to be thread-safe when all of the following
conditions are met:
The system's libc is itself thread-safe,
gcc -v reports a thread model other than 'single',
[pre-3.3 only] a non-generic implementation of atomicity.h exists for the architecture in question.
When an std::string is copied during call to log, the allocator may be thread-safe (mandatory in C++11), but the copy itself isn't. So if there is another thread mutating the source string while copy is taking place, this is not thread safe.
You may end-up with half the string as it was before mutation and another half after, or may even end-up accessing deallocated memory if the mutating thread reallocated (e.g. by appending new characters) or deleted the string, while the copy was still taking place.
OTOH, the...
std::string msg = "message";
...is thread safe provided your allocator is thread safe.