10

I'm using GCC 7.3.1, but also tested on coliru, which I believe is version 9.2.0. Build with the following:

g++ -fsanitize=address -fno-omit-frame-pointer rai.cpp

Here's rai.cpp:

#include <iostream>
#include <unordered_map>

int main()
{
    try
    {
        struct MyComp {
            bool operator()(const std::string&, const std::string&) const {
                throw std::runtime_error("Nonono");
            }
        };

        std::unordered_map<std::string, std::string, std::hash<std::string>, MyComp> mymap;
        mymap.insert(std::make_pair("Hello", "There"));
        mymap.insert(std::make_pair("Hello", "There")); // Hash match forces compare
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << "\n";
    }
}

Running it results in:

> ./a.out
Caught exception: Nonono

=================================================================
==72432==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 32 byte(s) in 1 object(s) allocated from:
...

Direct leak of 4 byte(s) in 1 object(s) allocated from:
...

Indirect leak of 60 byte(s) in 2 object(s) allocated from:
...

SUMMARY: AddressSanitizer: 96 byte(s) leaked in 4 allocation(s).

I don't see any memory leaks with Visual C++ (Microsoft (R) C/C++ Optimizing Compiler Version 19.24.28314 for x64).

Does this break the strong exception safety guarantee of unordered_map::insert (https://stackoverflow.com/a/11699271/1958315)? Is this a bug in the GCC STL?

Rai
  • 1,328
  • 11
  • 20
  • The STL is only going to catch exceptions it generates (if it can). It's not going to guard against you breaking it's invariant. Good CPPCON talk about it: https://www.youtube.com/watch?v=b9ZYM0d6htg&list=PLHTh1InhhwT6KhvViwRiTR7I5s09dLCSw&index=68&t=728s – NathanOliver Dec 12 '19 at 14:36
  • 1
    @NathanOliver-ReinstateMonica probably documentation needs to be updated then,as `std::unordered_map::insert` clearly says "1-4) If an exception is thrown by **any operation**, the insertion has no effect." (emphasis is mine) from here https://en.cppreference.com/w/cpp/container/unordered_map/insert – Slava Dec 12 '19 at 14:49
  • libc++ does not leak any memory when running this program. – Marshall Clow Dec 12 '19 at 14:56
  • @NathanOliver-ReinstateMonica that's nonsense. The standard library has to handle exceptions from types the user defines. There is no broken invariant here. – Jonathan Wakely Dec 12 '19 at 15:56
  • @Rai this is a bug, please report it https://gcc.gnu.org/bugs/ – Jonathan Wakely Dec 12 '19 at 15:57
  • It looks like it was recently fixed on trunk, probably by https://gcc.gnu.org/r272381 – Jonathan Wakely Dec 12 '19 at 16:02

1 Answers1

2

The guarantee mandated by the standard (quotes from latest draft):

[container.requirements.general]

Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following additional requirements:

  • if an exception is thrown by an insert() or emplace() function while inserting a single element, that function has no effects.

[associative.reqmts.except]

For associative containers, if an exception is thrown by any operation from within an insert or emplace function inserting a single element, the insertion has no effect.

[unord.req.except]

For unordered associative containers, if an exception is thrown by any operation other than the container's hash function from within an insert or emplace function inserting a single element, the insertion has no effect.

As far as I understand, "has no effect" implies "no memory leak". Under such interpretation, I would consider a leak to be a bug.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326