Skip to content

EDG's exception handling state is process-global, so concurrent threads corrupt it #446

Open

Description

@mborland

Issue Summary

With the C-generating back end, the exception handling state is held in
ordinary global variables: __curr_eh_stack_entry, __eh_curr_region and
__catch_clause_number (lib_src/eh.h:418-427). Generated code declares
extern struct ... *__curr_eh_stack_entry; and links its frames onto it.
Every function with a try block or objects that need cleanup pushes and pops
frames on this single shared stack, so threads running C++ code at the same
time interleave each other's frames. Throwing in two threads at once crashes
or trips assertions in the runtime. Threads that never throw can also leave
the stack pointing at another thread's popped frame.

In Boost, Boost.Unordered's cfoa_exception_erase_tests and
cfoa_exception_insert_tests abort with an assertion in lib_src/throw.c
on a worker thread. Several other multithreaded tests crash and are probably
the same issue (see below).

Reproducing Source Code

#include <stdexcept>
#include <thread>

static void work() {
  for (int i = 0; i < 100000; ++i) {
    try {
      throw std::runtime_error("x");
    } catch (const std::runtime_error &) {
    }
  }
}

int main() {
  std::thread a(work), b(work);
  a.join();
  b.join();
  return 0;
}

Command-line Options

EDG_USE_SYSTEM_HEADERS=1 eccp --c++17 eh_threads.cpp -o eh_threads && ./eh_threads

Type of Issue

[Back End] There is a problem with the generated C code.

Additional Details

Expected: exit status 0 (g++ 15.3.1: 0).
Actual: every run fails, with a different symptom each time:

  • linux-gcc-release, 3 runs: exit 134 with
    Assertion failed in file "/edg/workspace/lib_src/throw.c", line 1529;
    exit 134 with Assertion failed in file "/edg/workspace/lib_src/throw.c", line 481
    and C++ runtime abort: terminate() called by the exception handling mechanism;
    exit 134 with terminate() and Assertion failed in file "/edg/workspace/lib_src/throw.c", line 1228.
  • linux-gcc-debug, 3 runs: exit 134 (terminate()), then two segmentation
    faults (exit 139).

The problem is in the runtime library's design rather than in the generated C
as such; none of the issue types fits exactly. The state is created as a plain
external variable by make_curr_eh_stack_entry_var (src/lower_eh.c:4755,
sc_extern) and defined with EXTERN in lib_src/eh.h. The
*-multi-thread-* presets are about compiling with several front end threads
(MULTIPLE_THREAD_COMPILATION, EDG_FRONT_END_DAEMON), not about the
runtime. A fix would make this state thread-local (_Thread_local /
__thread) in the runtime and in the declaration the back end generates, on
targets that support TLS.

Boost:

  • Confirmed: libs/unordered/test cfoa_exception_erase_tests and
    cfoa_exception_insert_tests abort with
    Assertion failed in file "/edg/workspace/lib_src/throw.c", line 1481 inside
    __throw, called from stateful_hash::throw_helper on a std::thread
    started by the test's thread_runner.
  • Probably the same issue (not confirmed one by one): cfoa_rw_spinlock_test3/6/7/8
    (segmentation fault after "No errors detected"), and Boost.Regex
    regex_regress_threaded and regex_regress_threaded_no_cache.

Environment: edgcpp/dev-env Docker image (Fedora 43, GCC 15.3.1) on
x86-64, boost branch at c36e03a67c. EDG_USE_SYSTEM_HEADERS=1 makes eccp
use GCC 15's headers (libstdc++) and pass --gnu_version=150301
(bases/docker/dev-env/gcc/edg_eccp_config); eccp already links
-lpthread.

Reproducing (Standard) Configuration(s)

linux-gcc-release

Reproducing (Non-standard) Configuration

No response

I acknowledge that:

  • I have checked open issues and existing discussions before filing this report.
  • The reproducing source code that I have provided does not depend on headers that I have not included with my submission.
  • I have reduced the reproducing source code to the best of my ability.
  • I have selected at least one standard configuration or uploaded a non-standard configuration dump.
  • I understand code shared via this form is NOT confidential.
  • I have permission to share the reproducing source code and grant permission for the project to use the source code (or derivatives of the source code) to create tests.
  • I agree that any tests created using the reproducing source code (or deriatives of it) will be licensed under project license unless I included a LICENSE.txt file in my .zip or .tar.gz upload. If I included a LICENSE.txt file, any tests created using the reproducing source code will be governed by the terms of the license I provided instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions