Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: f2py threads.h on MacOS 14.x may need a shim? #27718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tylerjereddy opened this issue Nov 8, 2024 · 36 comments · Fixed by #27729
Closed

BUG: f2py threads.h on MacOS 14.x may need a shim? #27718

tylerjereddy opened this issue Nov 8, 2024 · 36 comments · Fixed by #27729

Comments

@tylerjereddy
Copy link
Contributor

Downstream in SciPy (see: scipy/scipy#21830), when using GNU toolchain version 14.2.0 from homebrew, I consistently see a SciPy build failure on MacOS ARM because of scipy/linalg/_flapackmodule.c:90:10: fatal error: threads.h: No such file or directory.

I made a temporary hack (below) to my local NumPy/f2py source that fixed the SciPy build issue. I'm not sure if that's exactly what we'd want to do, though GNU docs on the matter do suggest that threads.h is missing on MacOS 14. This part of f2py has caused pain a few times in the past per gh-19437 and gh-24761.

--- a/numpy/f2py/cfuncs.py
+++ b/numpy/f2py/cfuncs.py
@@ -558,7 +558,8 @@ def errmess(s: str) -> None:
       && (__STDC_VERSION__ >= 201112L) \\
       && !defined(__STDC_NO_THREADS__) \\
       && (!defined(__GLIBC__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 12)) \\
-      && !defined(NPY_OS_OPENBSD) && !defined(NPY_OS_HAIKU)
+      && !defined(NPY_OS_OPENBSD) && !defined(NPY_OS_HAIKU) \\
+      && !(defined(__APPLE__) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 && __MAC_OS_X_VERSION_MAX_ALLOWED < 150000))
 /* __STDC_NO_THREADS__ was first defined in a maintenance release of glibc 2.12,
    see https://lists.gnu.org/archive/html/commit-hurd/2012-07/msg00180.html,
    so `!defined(__STDC_NO_THREADS__)` may give false positive for the existence
@rgommers
Copy link
Member

rgommers commented Nov 8, 2024

I think it's time to throw away this code, it's way too hacky. We should be able to define F2PY_THREAD_LOCAL_DECL in the same way as NPY_TLS instead. That looks much cleaner:

#ifdef __cplusplus
#define NPY_TLS thread_local
#elif defined(HAVE_THREAD_LOCAL)
#define NPY_TLS thread_local
#elif defined(HAVE__THREAD_LOCAL)
#define NPY_TLS _Thread_local
#elif defined(HAVE___THREAD)
#define NPY_TLS __thread
#elif defined(HAVE___DECLSPEC_THREAD_)
#define NPY_TLS __declspec(thread)
#else
#define NPY_TLS
#endif

@rgommers
Copy link
Member

rgommers commented Nov 8, 2024

Hmm, not so easy unfortunately, that requires compile-time checks:

# variable attributes tested via "int %s a" % attribute
optional_variable_attributes = [
['thread_local', 'HAVE_THREAD_LOCAL'],
['_Thread_local', 'HAVE__THREAD_LOCAL'],
['__thread', 'HAVE__THREAD'],
['__declspec(thread)', 'HAVE___DECLSPEC_THREAD_']
]
foreach optional_attr: optional_variable_attributes
attr = optional_attr[0]
code = f'''
#pragma GCC diagnostic error "-Wattributes"
#pragma clang diagnostic error "-Wattributes"
int @attr@ foo;
'''
code += '''
int
main()
{
return 0;
}
'''
if cc.compiles(code, name: optional_attr[0])
cdata.set10(optional_attr[1], true)
endif
endforeach

That's more robust, but we can't require all f2py users to implement that (unconditionally at least).

@rgommers
Copy link
Member

rgommers commented Nov 8, 2024

@tylerjereddy is the only difference between success and failure using GCC 14 vs. 13 on your machine?

@tylerjereddy
Copy link
Contributor Author

@rgommers no, I was able to confirm that both of those GCC versions fail identically due to missing threads.h (via f2py), on OSX 14.7, Python 3.12.3, NumPy 2.1.3. So both of these incantations fail identically (with git clean -xfd in between):

  • CC=gcc-13 CXX=g++-13 python dev.py build -j 16 (Homebrew GCC 13.3.0)
  • CC=gcc-14 CXX=g++-14 python dev.py build -j 16 (Homebrew GCC 14.2.0_1)

I often use a fixed GNU FC version, but also just confirmed this also has the same behavior:
CC=gcc-13 CXX=g++-13 FC=gfortran-13 python dev.py build -j 16

@rgommers
Copy link
Member

rgommers commented Nov 8, 2024

With conda-forge compilers I currently use, I get this with both GCC 12.3.0 on Linux and Clang 16.0.6 on macOS 14.7:

/* #undef HAVE_THREAD_LOCAL */
#define HAVE__THREAD_LOCAL 1
#define HAVE__THREAD 1
/* #undef HAVE___DECLSPEC_THREAD_ */

Gating things only on OS version is clearly not right.

@tylerjereddy
Copy link
Contributor Author

Agreed, I don't replicate the problem with AppleClang (I only noticed it because MacPorts folks were complaining about GNU toolchain) so full suppression of the header include on OS 14.x seems a bit aggressive.

I don't know how much work it would be to get the guard "just right" (investigate clang version and GNU version ranges?).

@rgommers
Copy link
Member

rgommers commented Nov 8, 2024

I don't know how much work it would be to get the guard "just right" (investigate clang version and GNU version ranges?).

It's impossible to get it "just right" without build-time compiler checks AFAICT. I think what we should do (similar to what I suggested as workaround in #19437 (comment)) is to document how to do the compiler checks in the package using f2py and then adding the correct F2PY_THREAD_LOCAL_DECL define to the build flags.

Does anyone see a cleaner solution?

@barracuda156
Copy link

threads.h is also missing on earlier macOS, and the source erroneously assumes it is present due to C version.

[2/508] Compiling C object scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolativemodule.c.o
FAILED: scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolativemodule.c.o 
/opt/local/bin/gcc-mp-14 -Iscipy/linalg/_interpolative.cpython-311-darwin.so.p -Iscipy/linalg -I../scipy/linalg -I../../../../../../../../Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/numpy/core/include -I../../../../../../../../Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/numpy/f2py/src -I/opt/local/include/openblas -I/opt/local/Library/Frameworks/Python.framework/Versions/3.11/include/python3.11 -fvisibility=hidden -fdiagnostics-color=always -DNDEBUG -Wall -Winvalid-pch -std=c17 -O3 -Wno-unused-but-set-variable -Wno-unused-function -Wno-conversion -Wno-misleading-indentation -isysroot/ -MD -MQ scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolativemodule.c.o -MF scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolativemodule.c.o.d -o scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolativemodule.c.o -c scipy/linalg/_interpolative.cpython-311-darwin.so.p/_interpolativemodule.c
scipy/linalg/_interpolative.cpython-311-darwin.so.p/_interpolativemodule.c:96:10: fatal error: threads.h: No such file or directory
   96 | #include <threads.h>
      |          ^~~~~~~~~~~
compilation terminated.
[3/508] Compiling Fortran object scipy/linalg/_interpolative.cpython-311-darwin.so.p/meson-generated__interpolative-f2pywrappers.f.o
[4/508] Compiling C object scipy/linalg/_flapack.cpython-311-darwin.so.p/meson-generated_..__flapackmodule.c.o
FAILED: scipy/linalg/_flapack.cpython-311-darwin.so.p/meson-generated_..__flapackmodule.c.o 
/opt/local/bin/gcc-mp-14 -Iscipy/linalg/_flapack.cpython-311-darwin.so.p -Iscipy/linalg -I../scipy/linalg -I../../../../../../../../Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/numpy/core/include -I../../../../../../../../Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/numpy/f2py/src -I/opt/local/include/openblas -I/opt/local/Library/Frameworks/Python.framework/Versions/3.11/include/python3.11 -fvisibility=hidden -fdiagnostics-color=always -DNDEBUG -Wall -Winvalid-pch -std=c17 -O3 -Wno-unused-but-set-variable -Wno-unused-function -Wno-conversion -Wno-misleading-indentation -isysroot/ -Wno-empty-body -MD -MQ scipy/linalg/_flapack.cpython-311-darwin.so.p/meson-generated_..__flapackmodule.c.o -MF scipy/linalg/_flapack.cpython-311-darwin.so.p/meson-generated_..__flapackmodule.c.o.d -o scipy/linalg/_flapack.cpython-311-darwin.so.p/meson-generated_..__flapackmodule.c.o -c scipy/linalg/_flapackmodule.c
scipy/linalg/_flapackmodule.c:90:10: fatal error: threads.h: No such file or directory
   90 | #include <threads.h>
      |          ^~~~~~~~~~~
compilation terminated.

@rgommers
Copy link
Member

rgommers commented Nov 10, 2024

threads.h is also missing on earlier macOS, and the source erroneously assumes it is present due to C version.

Yes, this is correct indeed, and the bug is that __STDC_NO_THREADS__ isn't set despite no threads.h. This is the compilers job I believe (e.g., MSVC takes care of defining it), so it's the GCC build in use that has the bug.

I think this was probably always broken, but no one noticed because they weren't yet compiling with -std=c11 or higher (plus using GCC on macOS is rare).

@rgommers
Copy link
Member

Looks like the C standards committee saw the error of its ways - in C23 thread_local is removed from threads.h and becomes a C keyword instead: https://en.cppreference.com/w/c/thread/thread_local.

@barracuda156
Copy link

barracuda156 commented Nov 10, 2024

threads.h is also missing on earlier macOS, and the source erroneously assumes it is present due to C version.

Yes, this is correct indeed, and the bug is that __STDC_NO_THREADS__ isn't set despite no threads.h. This is the compilers job I believe (e.g., MSVC takes care of defining it), so it's the GCC build in use that has the bug.

Discussion with gcc upstream: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769

I think this was probably always broken, but no one noticed because they weren't yet compiling with -std=c11 or higher (plus using GCC on macOS is rare).

I think this is just untypical to use this header. GCC supports emulated TLS on systems where it is not supported otherwise (and recent macOS support TLS in the OS).

@rgommers
Copy link
Member

GCC supports emulated TLS on systems where it is not supported otherwise (and recent macOS support TLS in the OS).

You mean GCC and/or macOS recognize thread_local without any header include? Or it expects you to use _Thread_local?

@barracuda156
Copy link

_Thread_local

I think gcc normally uses this one, yes.

Re emutls, I do not know internals of the implementation, but gcc have some docs on it.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

Is there any real value in using "real" gcc to build things on macOS? As soon as your code uses macOS headers, you hit the infamous "blocks" feature, not implemented in gcc. (e.g. SageMath stopped using gcc on macOS ages ago).

@barracuda156
Copy link

@dimpase Blocks are only used in ObjC, which is a minuscule part of open-source software. (Besides, gcc upstream plans to support blocks somewhat soon, though I am not aware of intended timeline.)

Also it is good for code quality. GCC is one of the two major compilers with its own C++ runtime. Quality code should work with it on whatever system, at least as long as it is C/C++/Fortran.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

No, macOS headers have blocks in plain C as well. Anything related to cryptography on macOS will need to use them; e.g. you can't even build a fully functioning Python using gcc only (you'd hit problems with urllib, IIRC).

I certainly agree with the code quality point.

@barracuda156
Copy link

you can't even build a fully functioning Python

I do not know how is “fully” defined, but I have certainly built Python with gcc on macOS, and it was functioning.

Anything related to cryptography on macOS will need to use them

You mean their cryptography? That is unsurprising, since it uses ObjC (used at least, and perhaps nothing much changed). But there are plenty of open-source cryptography solutions not relying on Apple ObjC extensions.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

I do not know how is “fully” defined, but I have certainly built Python with gcc on macOS, and it was functioning.

how about installing packages from PyPI ? (That is, pip etc. should be built with "real" gcc too).

@barracuda156
Copy link

I do not know how is “fully” defined, but I have certainly built Python with gcc on macOS, and it was functioning.

how about installing packages from PyPI ? (That is, pip etc. should be built with "real" gcc too).

I use MacPorts, I do not think anything fails with Python as such or C/C++ code in Python ports. Rust needs LLVM, so that won’t work with gcc, and there a few select packages which heavily depend on Apple SDK (and perhaps Apple ObjC syntax).

BTW, modern gcc supports libc++, so it can be used with Apple C++ native runtime, if one wishes to.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

modern gcc supports libc++

in theory at least, clang supports using libstdc++, so one can use any of the two compilers with any of the two C++ standard libraries.

@barracuda156
Copy link

modern gcc supports libc++

in theory at least, clang supports using libstdc++, so one can use any of the two compilers with any of the two C++ standard libraries.

In fact Apple clang did, I think up to 10.8 libstdc++ was the default runtime (despite appearance of libc++ in 10.7). Never heard anyone using modern clang with libstdc++ though, at least on macOS.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

Never heard anyone using modern clang with libstdc++ though, at least on macOS.

Isn't this the default on Linux, to use libstdc++ with clang ? Cf. e.g. https://wiki.gentoo.org/wiki/Clang#Configuration

One way or another, libstdc++ on a modern macOS will make it near-impossible to use OS libraries. So this has a purely academic value, at most.

@rgommers
Copy link
Member

There is certainly value in building with GCC on macOS, it should work more robustly. For SciPy we rely on Gfortran - no alternative, since Apple doesn't ship a Fortran compiler.

I'll have a go at fixing this up now in both NumPy and SciPy.

@dimpase
Copy link
Contributor

dimpase commented Nov 10, 2024

we rely on Gfortran

sure, that's what everyone does, there is no reliable open-source alternative, as far as I know.

@rgommers
Copy link
Member

Attempted fix in gh-27729. @barracuda156 or @tylerjereddy it'd be useful if one of you could confirm that this indeed closes the issue.

@barracuda156
Copy link

@rgommers For some reason I do not get an error with numpy without patching this, maybe MacPorts uses the older numpy which does not have the issue? It is at 1.26.4 now.

For scipy the fix is needed though.

@rgommers
Copy link
Member

@barracuda156 that makes sense; f2py isn't used by numpy itself at build time, since there is no Fortran code in numpy. My question was indeed if it fixes the scipy issue, which you confirmed (and @tylerjereddy did as well, on the PR), so all good here.

@barracuda156
Copy link

@rgommers Wait, I did not yet test scipy exactly with your patch (though I think it should work).
Do I need to rebuild numpy with this patch first and then build scipy, or just apply the patch to a corresponding spot in scipy without rebuilding numpy?

@rgommers
Copy link
Member

Do I need to rebuild numpy with this patch first and then build scipy

Yes, this indeed. The scipy 1.14.1 build will be fixed when building against a numpy with this patch included.

or just apply the patch to a corresponding spot in scipy without rebuilding numpy?

This isn't an option, since the patch only changes the f2py-generated C code that is used by scipy to build a number of SciPy's extension modules.

@barracuda156
Copy link

Turns out that numpy 1.26.4 had the code which did not cause issues to begin with:

cppmacros["F2PY_THREAD_LOCAL_DECL"] = """
#ifndef F2PY_THREAD_LOCAL_DECL
#if defined(_MSC_VER)
#define F2PY_THREAD_LOCAL_DECL __declspec(thread)
#elif defined(NPY_OS_MINGW)
#define F2PY_THREAD_LOCAL_DECL __thread
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#define F2PY_THREAD_LOCAL_DECL _Thread_local
#elif defined(__GNUC__) \\
      && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)))
#define F2PY_THREAD_LOCAL_DECL __thread
#endif
#endif
"""

This should just pick _Thread_local with gcc, which works.

The patch does not apply onto 1.26.4, and numpy/_core/meson.build does not even exist yet.

I could try to build a suitably newer version of numpy, but that may require a bit of time to sort it out, rebase patches, possibly update some dependencies.

@barracuda156
Copy link

@ rgommers Or will it suffice for the testing purpose just to edit manually the script which numpy installs?
This file has the related code: /opt/local/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/numpy/f2py/cfuncs.py (and it has <threads.h> and the rest).

Instead of passing __STDC_NO_THREADS__ to scipy build I can edit cfuncs.py and try building scipy

@rgommers
Copy link
Member

@barracuda156 yes manually editing cfuncs.py will work - that seems much easier indeed.

@barracuda156
Copy link

Running the build now, will update in a while.

@barracuda156
Copy link

barracuda156 commented Nov 12, 2024

@rgommers Yes, it works as expected. Thank you!

@rgommers
Copy link
Member

Great - and thanks for all your help!

barracuda156 added a commit to barracuda156/powerpc-ports that referenced this issue Nov 13, 2024
Instead of adding __STDC_NO_THREADS__ flag into scipy,
we borrow upstream fix for numpy f2py module which scipy uses.
See: numpy/numpy#27718
@rgommers rgommers added this to the 2.2.0 release milestone Nov 13, 2024
reneeotten pushed a commit to macports/macports-ports that referenced this issue Nov 13, 2024
Instead of adding __STDC_NO_THREADS__ flag into scipy,
we borrow upstream fix for numpy f2py module which scipy uses.
See: numpy/numpy#27718
@dimpase
Copy link
Contributor

dimpase commented Nov 27, 2024

@dimpase Blocks are only used in ObjC, which is a minuscule part of open-source software. (Besides, gcc upstream plans to support blocks somewhat soon, though I am not aware of intended timeline.)

Also it is good for code quality. GCC is one of the two major compilers with its own C++ runtime. Quality code should work with it on whatever system, at least as long as it is C/C++/Fortran.

PS. I don't know what makes you think that blocks are only used in ObjC (in macOS or in general).
Please see https://en.wikipedia.org/wiki/Blocks_(C_language_extension)
In @sagemath we switched to clang on macOS many years ago, due to gcc being unable to compile C/C++ code using Apple's macOS headers in a nontrivial way.

ArvidJB pushed a commit to ArvidJB/numpy that referenced this issue Jan 8, 2025
This is a continuing source of issues, therefore in this commit we:
- stop using `threads.h` completely,
- better document what is happening, and
- ensure the f2py TLS define stays unchanged so users can override
  it at build time by passing the define as a compile flag.

Closes numpygh-27718
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants