Skip to content

[coroutines] Clang incorrectly caches result of pthread_self() across coroutine suspend-points #47177

Closed
@lewissbaker

Description

@lewissbaker
Bugzilla Link 47833
Version trunk
OS Linux
CC @zygoloid

Extended Description

Godbolt: https://godbolt.org/z/zYfvaM

Compile the following program with: -std=c++20 -fcoroutines-ts -stdlib=libc++ -O2 -pthread

#include <experimental/coroutine>
#include <thread>
#include <unistd.h>
#include <pthread.h>
 
auto switch_to_new_thread(std::thread& out) {
  struct awaitable {
    std::thread* p_out;
    bool await_ready() { return false; }
    void await_suspend(std::experimental::coroutine_handle<> h) {
      std::thread& out = *p_out;
      out = std::thread([h]() mutable {
          h.resume();
        });
    }
    void await_resume() {}
  };
  return awaitable{&out};
}
 
struct task{
  struct promise_type {
    task get_return_object() { return {}; }
    std::experimental::suspend_never initial_suspend() { return {}; }
    std::experimental::suspend_never final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };
};

static task resuming_on_new_thread(std::thread& out) {
  auto id1 = std::this_thread::get_id();
  auto pthread1 = pthread_self();
  auto pid1 = gettid();

  co_await switch_to_new_thread(out);

  auto id2 = std::this_thread::get_id();
  auto pthread2 = pthread_self();
  auto pid2 = gettid();

  std::puts((id1 == id2) ? "this_thread::get_id() was same" : "this_thread::get_id() was different");
  std::puts((pid1 == pid2) ? "gettid() was same" : "gettid() was different");
  std::puts((pthread1 == pthread2) ? "pthread_self() was same" : "pthread_self() was different");
}
 
int main() {
  std::thread out;
  resuming_on_new_thread(out);
  out.join();
}

Expected output:

this_thread::get_id() was different
gettid() was different
pthread_self() was different

Actual output:

this_thread::get_id() was same
gettid() was different
pthread_self() was same

The compiler generates code that calls the pthread_self() function once at the start of the coroutine and then caches the result in the coroutine frame and reloads the cached value after the coroutine resumes on another thread, leading to the second call to pthread_self() seemingly returning the wrong value.

This also affects std::this_thread::get_id(), which is a thin-wrapper over pthread_self(), and so if get_id() is inlined into the coroutine then it hits the same problem. If get_id() is not inlined into the coroutine (eg. when using -O1) then the second std::this_thread::get_id() gives the correct result.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions