Skip to content

Commit fbdf684

Browse files
cebowlerldionne
andcommitted
[libc++] Avoid destructor call for error_category singletons
When a handle to an error_category singleton object is used during the termination phase of a program, the destruction of the error_category object may have occurred prior to execution of the current destructor or function registered with atexit, because the singleton object may have been constructed after the corresponding initialization or call to atexit. For example, the updated tests from this patch will fail if using a libc++ built using a compiler that updates the vtable of the object on destruction. This patch attempts to avoid the issue by causing the destructor to not be called in the style of ResourceInitHelper in src/experimental/memory_resource.cpp. This approach might not work if object lifetime is strictly enforced. Differential Revision: https://reviews.llvm.org/D65667 Co-authored-by: Louis Dionne <[email protected]>
1 parent e643695 commit fbdf684

File tree

9 files changed

+176
-86
lines changed

9 files changed

+176
-86
lines changed

libcxx/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(LIBCXX_SOURCES
1111
chrono.cpp
1212
condition_variable.cpp
1313
condition_variable_destructor.cpp
14+
error_category.cpp
1415
exception.cpp
1516
filesystem/filesystem_clock.cpp
1617
filesystem/filesystem_error.cpp

libcxx/src/error_category.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <__config>
10+
11+
#ifdef _LIBCPP_DEPRECATED_ABI_LEGACY_LIBRARY_DEFINITIONS_FOR_INLINE_FUNCTIONS
12+
# define _LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS
13+
#endif
14+
15+
#include <system_error>
16+
17+
_LIBCPP_BEGIN_NAMESPACE_STD
18+
19+
// class error_category
20+
21+
#if defined(_LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS)
22+
error_category::error_category() noexcept {}
23+
#endif
24+
25+
error_category::~error_category() noexcept {}
26+
27+
error_condition error_category::default_error_condition(int ev) const noexcept { return error_condition(ev, *this); }
28+
29+
bool error_category::equivalent(int code, const error_condition& condition) const noexcept {
30+
return default_error_condition(code) == condition;
31+
}
32+
33+
bool error_category::equivalent(const error_code& code, int condition) const noexcept {
34+
return *this == code.category() && code.value() == condition;
35+
}
36+
37+
_LIBCPP_END_NAMESPACE_STD

libcxx/src/future.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,13 @@ _LIBCPP_DIAGNOSTIC_POP
5959
const error_category&
6060
future_category() noexcept
6161
{
62-
static __future_error_category __f;
63-
return __f;
62+
union AvoidDestroyingFutureCategory {
63+
__future_error_category future_error_category;
64+
constexpr explicit AvoidDestroyingFutureCategory() : future_error_category() {}
65+
~AvoidDestroyingFutureCategory() {}
66+
};
67+
constinit static AvoidDestroyingFutureCategory helper;
68+
return helper.future_error_category;
6469
}
6570

6671
future_error::future_error(error_code __ec)

libcxx/src/ios.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,13 @@ __iostream_category::message(int ev) const
5252
const error_category&
5353
iostream_category() noexcept
5454
{
55-
static __iostream_category s;
56-
return s;
55+
union AvoidDestroyingIostreamCategory {
56+
__iostream_category iostream_error_category;
57+
constexpr explicit AvoidDestroyingIostreamCategory() : iostream_error_category() {}
58+
~AvoidDestroyingIostreamCategory() {}
59+
};
60+
constinit static AvoidDestroyingIostreamCategory helper;
61+
return helper.iostream_error_category;
5762
}
5863

5964
// ios_base::failure

libcxx/src/system_error.cpp

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include <__config>
10-
#ifdef _LIBCPP_DEPRECATED_ABI_LEGACY_LIBRARY_DEFINITIONS_FOR_INLINE_FUNCTIONS
11-
# define _LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS
12-
#endif
13-
149
#include <__assert>
10+
#include <__config>
1511
#include <__verbose_abort>
1612
#include <cerrno>
1713
#include <cstdio>
@@ -29,36 +25,6 @@
2925

3026
_LIBCPP_BEGIN_NAMESPACE_STD
3127

32-
// class error_category
33-
34-
#if defined(_LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS)
35-
error_category::error_category() noexcept
36-
{
37-
}
38-
#endif
39-
40-
error_category::~error_category() noexcept
41-
{
42-
}
43-
44-
error_condition
45-
error_category::default_error_condition(int ev) const noexcept
46-
{
47-
return error_condition(ev, *this);
48-
}
49-
50-
bool
51-
error_category::equivalent(int code, const error_condition& condition) const noexcept
52-
{
53-
return default_error_condition(code) == condition;
54-
}
55-
56-
bool
57-
error_category::equivalent(const error_code& code, int condition) const noexcept
58-
{
59-
return *this == code.category() && code.value() == condition;
60-
}
61-
6228
namespace {
6329
#if !defined(_LIBCPP_HAS_NO_THREADS)
6430

@@ -186,8 +152,13 @@ __generic_error_category::message(int ev) const
186152
const error_category&
187153
generic_category() noexcept
188154
{
189-
static __generic_error_category s;
190-
return s;
155+
union AvoidDestroyingGenericCategory {
156+
__generic_error_category generic_error_category;
157+
constexpr explicit AvoidDestroyingGenericCategory() : generic_error_category() {}
158+
~AvoidDestroyingGenericCategory() {}
159+
};
160+
constinit static AvoidDestroyingGenericCategory helper;
161+
return helper.generic_error_category;
191162
}
192163

193164
class _LIBCPP_HIDDEN __system_error_category
@@ -228,8 +199,13 @@ __system_error_category::default_error_condition(int ev) const noexcept
228199
const error_category&
229200
system_category() noexcept
230201
{
231-
static __system_error_category s;
232-
return s;
202+
union AvoidDestroyingSystemCategory {
203+
__system_error_category system_error_category;
204+
constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {}
205+
~AvoidDestroyingSystemCategory() {}
206+
};
207+
constinit static AvoidDestroyingSystemCategory helper;
208+
return helper.system_error_category;
233209
}
234210

235211
// error_condition

libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/generic_category.pass.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,43 @@
2222

2323
#include "test_macros.h"
2424

25-
void test_message_for_bad_value() {
26-
errno = E2BIG; // something that message will never generate
27-
const std::error_category& e_cat1 = std::generic_category();
28-
const std::string msg = e_cat1.message(-1);
29-
// Exact message format varies by platform.
25+
// See https://llvm.org/D65667
26+
struct StaticInit {
27+
const std::error_category* ec;
28+
~StaticInit() {
29+
std::string str = ec->name();
30+
assert(str == "generic") ;
31+
}
32+
};
33+
static StaticInit foo;
34+
35+
int main(int, char**)
36+
{
37+
{
38+
const std::error_category& e_cat1 = std::generic_category();
39+
std::string m1 = e_cat1.name();
40+
assert(m1 == "generic");
41+
}
42+
43+
// Test the result of message(int cond) when given a bad error condition
44+
{
45+
errno = E2BIG; // something that message will never generate
46+
const std::error_category& e_cat1 = std::generic_category();
47+
const std::string msg = e_cat1.message(-1);
48+
// Exact message format varies by platform.
3049
#if defined(_AIX)
31-
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
50+
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
3251
#else
33-
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
52+
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
3453
#endif
35-
assert(errno == E2BIG);
36-
}
54+
assert(errno == E2BIG);
55+
}
3756

38-
int main(int, char**)
39-
{
40-
const std::error_category& e_cat1 = std::generic_category();
41-
std::string m1 = e_cat1.name();
42-
assert(m1 == "generic");
4357
{
44-
test_message_for_bad_value();
58+
foo.ec = &std::generic_category();
59+
std::string m2 = foo.ec->name();
60+
assert(m2 == "generic");
4561
}
4662

47-
return 0;
63+
return 0;
4864
}

libcxx/test/std/diagnostics/syserr/syserr.errcat/syserr.errcat.objects/system_category.pass.cpp

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,47 @@
2222

2323
#include "test_macros.h"
2424

25-
void test_message_for_bad_value() {
26-
errno = E2BIG; // something that message will never generate
27-
const std::error_category& e_cat1 = std::system_category();
28-
const std::string msg = e_cat1.message(-1);
29-
// Exact message format varies by platform.
25+
// See https://llvm.org/D65667
26+
struct StaticInit {
27+
const std::error_category* ec;
28+
~StaticInit() {
29+
std::string str = ec->name();
30+
assert(str == "system") ;
31+
}
32+
};
33+
static StaticInit foo;
34+
35+
int main(int, char**)
36+
{
37+
{
38+
const std::error_category& e_cat1 = std::system_category();
39+
std::error_condition e_cond = e_cat1.default_error_condition(5);
40+
assert(e_cond.value() == 5);
41+
assert(e_cond.category() == std::generic_category());
42+
e_cond = e_cat1.default_error_condition(5000);
43+
assert(e_cond.value() == 5000);
44+
assert(e_cond.category() == std::system_category());
45+
}
46+
47+
// Test the result of message(int cond) when given a bad error condition
48+
{
49+
errno = E2BIG; // something that message will never generate
50+
const std::error_category& e_cat1 = std::system_category();
51+
const std::string msg = e_cat1.message(-1);
52+
// Exact message format varies by platform.
3053
#if defined(_AIX)
31-
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
54+
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
3255
#else
33-
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
56+
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
3457
#endif
35-
assert(errno == E2BIG);
36-
}
58+
assert(errno == E2BIG);
59+
}
3760

38-
int main(int, char**)
39-
{
40-
const std::error_category& e_cat1 = std::system_category();
41-
std::error_condition e_cond = e_cat1.default_error_condition(5);
42-
assert(e_cond.value() == 5);
43-
assert(e_cond.category() == std::generic_category());
44-
e_cond = e_cat1.default_error_condition(5000);
45-
assert(e_cond.value() == 5000);
46-
assert(e_cond.category() == std::system_category());
4761
{
48-
test_message_for_bad_value();
62+
foo.ec = &std::system_category();
63+
std::string m = foo.ec->name();
64+
assert(m == "system");
4965
}
5066

51-
return 0;
67+
return 0;
5268
}

libcxx/test/std/input.output/iostreams.base/std.ios.manip/error.reporting/iostream_category.pass.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,29 @@
1616

1717
#include "test_macros.h"
1818

19+
// See https://llvm.org/D65667
20+
struct StaticInit {
21+
const std::error_category* ec;
22+
~StaticInit() {
23+
std::string str = ec->name();
24+
assert(str == "iostream") ;
25+
}
26+
};
27+
static StaticInit foo;
28+
1929
int main(int, char**)
2030
{
21-
const std::error_category& e_cat1 = std::iostream_category();
22-
std::string m1 = e_cat1.name();
23-
assert(m1 == "iostream");
31+
{
32+
const std::error_category& e_cat1 = std::iostream_category();
33+
std::string m1 = e_cat1.name();
34+
assert(m1 == "iostream");
35+
}
36+
37+
{
38+
foo.ec = &std::iostream_category();
39+
std::string m2 = foo.ec->name();
40+
assert(m2 == "iostream");
41+
}
2442

25-
return 0;
43+
return 0;
2644
}

libcxx/test/std/thread/futures/futures.errors/future_category.pass.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,26 @@
1818

1919
#include "test_macros.h"
2020

21+
// See https://llvm.org/D65667
22+
struct StaticInit {
23+
const std::error_category* ec;
24+
~StaticInit() {
25+
assert(std::strcmp(ec->name(), "future") == 0);
26+
}
27+
};
28+
static StaticInit foo;
29+
2130
int main(int, char**)
2231
{
23-
const std::error_category& ec = std::future_category();
24-
assert(std::strcmp(ec.name(), "future") == 0);
32+
{
33+
const std::error_category& ec = std::future_category();
34+
assert(std::strcmp(ec.name(), "future") == 0);
35+
}
36+
37+
{
38+
foo.ec = &std::future_category();
39+
assert(std::strcmp(foo.ec->name(), "future") == 0);
40+
}
2541

26-
return 0;
42+
return 0;
2743
}

0 commit comments

Comments
 (0)