Skip to content

Make _Py_ThreadId() work on PowerPC, IBM Z, etc. #112535

Closed

Description

@colesbury

Feature or enhancement

Currently, _Py_ThreadId() is only defined in Py_GIL_DISABLED builds and does not compile on PowerPC or IBM Z. It would be convenient to make the functionality available in general: it will be useful to implement thread-safe recursive locks. (Currently our recursive lock implementations rely on the GIL for thread-safety.)

There are two parts to this:

  1. We should use __builtin_thread_pointer() when available
  2. We should fall back to a function that returns the address of a thread-local variable if no other fast implementation is available (instead of erroring out).

The fallback (2) will be a bit slower, but it provides a portable implementation.

Note that checking if __builtin_thread_pointer() is available is not trivial. __has_builtin(__builtin_thread_pointer) is not useful (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96952). You need some combination of platform and GCC/clang version check.

cc @vstinner @corona10

Linked PRs

Activity

  1. added a commit that references this issue on Dec 4, 2023
  2. added 6 commits that reference this issue on Dec 4, 2023
  3. corona10 commented on Dec 8, 2023

    @corona10
    Member

    @colesbury Do we need to support more platforms or we can close the issue?

  4. colesbury commented on Dec 8, 2023

    @colesbury
    ContributorAuthor

    I think we still want the cross-platform fallback that returns the address of a thread-local variable. That will probably need to be implemented in a .c file because accessing thread-local variables across libraries can cause issues on some platforms.

  5. corona10 commented on Dec 8, 2023

    @corona10
    Member

    FYI, PEP 11 defines which platform we should cover :)
    According to PEP 11, WASM is left but IIRC, Support thread feature from WASM side is quite early stage: https://github.com/WebAssembly/wasi-threads
    https://peps.python.org/pep-0011/

  6. colesbury commented on Dec 8, 2023

    @colesbury
    ContributorAuthor

    Adding a fallback will ease the maintenance burden and also work with WASM. In general, I think we want portable implementations when possible even if the platforms listed in PEP 11 ought to work without them.

    We can't use _Py_ThreadId() outside of the --disable-gil builds right now. That makes it harder to implement an efficient recursive lock. We still will not want to expose _Py_ThreadId() outside of --disable-gil builds, but it will be convenient to use it in .c files like Python/lock.c.

  7. corona10 commented on Dec 10, 2023

    @corona10
    Member

    #ifdef __APPLE__
    uint64_t native_id;
    (void) pthread_threadid_np(NULL, &native_id);
    #elif defined(__linux__)
    pid_t native_id;
    native_id = syscall(SYS_gettid);
    #elif defined(__FreeBSD__)
    int native_id;
    native_id = pthread_getthreadid_np();
    #elif defined(__FreeBSD_kernel__)
    long native_id;
    syscall(SYS_thr_self, &native_id);
    #elif defined(__OpenBSD__)
    pid_t native_id;
    native_id = getthrid();
    #elif defined(_AIX)
    tid_t native_id;
    native_id = thread_self();
    #elif defined(__NetBSD__)
    lwpid_t native_id;
    native_id = _lwp_self();
    #elif defined(__DragonFly__)
    lwpid_t native_id;
    native_id = lwp_gettid();
    #endif
    return (unsigned long) native_id;
    }

    Well, we already implemented PyThread_get_thread_native_id for getting native thread id. Do you want to use same approach as the fallback?
    If yes, Which platforms do we have to care about? (Linux Only? or other things too?)

  8. colesbury commented on Dec 10, 2023

    @colesbury
    ContributorAuthor

    @corona10, no I don't think we should use PyThread_get_thread_native_id. I think we should do what I wrote as part (2) in the issue: "return the address of a thread-local variable"

  9. corona10 commented on Dec 10, 2023

    @corona10
    Member

    Got it, I will send a PR soon. I read TLS spec in here and now I understand what you want to say.(maybe?)

  10. colesbury commented on Dec 10, 2023

    @colesbury
    ContributorAuthor

    @corona10, here is an example of what I mean in mimalloc, which uses the same strategy on platforms where it doesn't implement a faster _mi_prim_thread_id():

    extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate from

    static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept {
    return (uintptr_t)&_mi_heap_default;
    }

    My reasons for preferring this strategy instead of PyThread_get_thread_native_id() are:

    1. It should be a lot faster than PyThread_get_thread_native_id() (no syscalls), although still not as fast as the platform-specific implementations currently used in _PyThread_Id()
    2. We can make more assumptions about the format (i.e., non-zero, pointer-aligned), which I think will come in handy when implementing recursive mutexes.
  11. vstinner commented on Dec 11, 2023

    @vstinner
    Member

    You may have a look at _PyThreadState_GET() which is implemented by reading a thread local variable. If Py_BUILD_CORE_MODULE macro is defined, the function is implemented with an opaque function call: implementation for libraries.

  12. corona10 commented on Dec 14, 2023

    @corona10
    Member

    Got it, I will send a PR soon.

    I am going to submit the PR early next week :)

  13. added 8 commits that reference this issue on Dec 14, 2023
  14. corona10 commented on Dec 18, 2023

    @corona10
    Member

    Okay, let's close the issue since the fallback implementation is merged.
    Thank you for all your supports and contribution @colesbury, @vstinner(Awesome test codes!) and @furkanonder (RISC-V)

    Now, we can support free-threaded CPython for all platforms where the fallback implementation(it only requires thread-local feature) works!

  15. added a commit that references this issue on Dec 26, 2023
  16. added 6 commits that reference this issue on Feb 11, 2024
  17. added 6 commits that reference this issue on Sep 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions