Skip to content

Commit

Permalink
Remove a layer of compiler-compatibility macros no longer relevant wi…
Browse files Browse the repository at this point in the history
…th the removal of Python 2.7.

Also requires a compiler that supports a 'noinline' directive.
  • Loading branch information
jamadden committed Jun 20, 2023
1 parent 642e3e5 commit f3574c4
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 177 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Expand Up @@ -25,6 +25,8 @@
raphaelauv, Hugo van Kemenade, Mark Shannon, and Petr Viktorin.
- Remove support for end-of-life Python versions, including Python
2.7, Python 3.5 and Python 3.6.
- Require a compiler that supports ``noinline`` directives. See
`issue 271 <https://github.com/python-greenlet/greenlet/issues/266>`_


2.0.2 (2023-01-28)
Expand Down
31 changes: 15 additions & 16 deletions src/greenlet/greenlet.cpp
Expand Up @@ -152,7 +152,7 @@ greenlet::refs::_BorrowedGreenlet<T, TC>& greenlet::refs::_BorrowedGreenlet<T, T
}

template <typename T, greenlet::refs::TypeChecker TC>
inline greenlet::refs::_BorrowedGreenlet<T, TC>::operator Greenlet*() const G_NOEXCEPT
inline greenlet::refs::_BorrowedGreenlet<T, TC>::operator Greenlet*() const noexcept
{
if (!this->p) {
return nullptr;
Expand All @@ -169,7 +169,7 @@ greenlet::refs::_BorrowedGreenlet<T, TC>::_BorrowedGreenlet(const BorrowedObject
}

template <typename T, greenlet::refs::TypeChecker TC>
inline greenlet::refs::_OwnedGreenlet<T, TC>::operator Greenlet*() const G_NOEXCEPT
inline greenlet::refs::_OwnedGreenlet<T, TC>::operator Greenlet*() const noexcept
{
if (!this->p) {
return nullptr;
Expand Down Expand Up @@ -790,26 +790,26 @@ MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state)
}

ThreadState*
MainGreenlet::thread_state() const G_NOEXCEPT
MainGreenlet::thread_state() const noexcept
{
return this->_thread_state;
}

void
MainGreenlet::thread_state(ThreadState* t) G_NOEXCEPT
MainGreenlet::thread_state(ThreadState* t) noexcept
{
assert(!t);
this->_thread_state = t;
}

BorrowedGreenlet
UserGreenlet::self() const G_NOEXCEPT
UserGreenlet::self() const noexcept
{
return this->_self;
}

BorrowedGreenlet
MainGreenlet::self() const G_NOEXCEPT
MainGreenlet::self() const noexcept
{
return BorrowedGreenlet(this->_self.borrow());
}
Expand Down Expand Up @@ -916,7 +916,7 @@ g_handle_exit(const OwnedObject& greenlet_result);
* argument dict. Otherwise, we'll create a tuple of (args, kwargs) and
* return both.
*/
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) G_NOEXCEPT
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept
{
// Because this may invoke arbitrary Python code, which could
// result in switching back to us, we need to get the
Expand Down Expand Up @@ -1005,7 +1005,7 @@ UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_threa
}

ThreadState*
UserGreenlet::thread_state() const G_NOEXCEPT
UserGreenlet::thread_state() const noexcept
{
// TODO: maybe make this throw, if the thread state isn't there?
// if (!this->main_greenlet) {
Expand All @@ -1020,19 +1020,19 @@ UserGreenlet::thread_state() const G_NOEXCEPT


bool
UserGreenlet::was_running_in_dead_thread() const G_NOEXCEPT
UserGreenlet::was_running_in_dead_thread() const noexcept
{
return this->_main_greenlet && !this->thread_state();
}

bool
MainGreenlet::was_running_in_dead_thread() const G_NOEXCEPT
MainGreenlet::was_running_in_dead_thread() const noexcept
{
return !this->_thread_state;
}

inline void
Greenlet::slp_restore_state() G_NOEXCEPT
Greenlet::slp_restore_state() noexcept
{
#ifdef SLP_BEFORE_RESTORE_STATE
SLP_BEFORE_RESTORE_STATE();
Expand All @@ -1043,7 +1043,7 @@ Greenlet::slp_restore_state() G_NOEXCEPT


inline int
Greenlet::slp_save_state(char *const stackref) G_NOEXCEPT
Greenlet::slp_save_state(char *const stackref) noexcept
{
// XXX: This used to happen in the middle, before saving, but
// after finding the next owner. Does that matter? This is
Expand Down Expand Up @@ -1175,7 +1175,7 @@ MainGreenlet::g_switch()


OwnedGreenlet
Greenlet::g_switchstack_success() G_NOEXCEPT
Greenlet::g_switchstack_success() noexcept
{
PyThreadState* tstate = PyThreadState_GET();
// restore the saved state
Expand Down Expand Up @@ -1413,7 +1413,7 @@ UserGreenlet::inner_bootstrap(OwnedGreenlet& origin_greenlet, OwnedObject& _run)
// It gets more complicated than that, though, on some
// platforms, specifically at least Linux/gcc/libstdc++. They use
// an exception to unwind the stack when a background
// thread exits. (See comments about G_NOEXCEPT.) So this
// thread exits. (See comments about noexcept.) So this
// may not actually represent anything untoward. On those
// platforms we allow throws of this to propagate, or
// attempt to anyway.
Expand Down Expand Up @@ -3139,10 +3139,9 @@ static struct PyModuleDef greenlet_module_def = {


static PyObject*
greenlet_internal_mod_init() G_NOEXCEPT
greenlet_internal_mod_init() noexcept
{
static void* _PyGreenlet_API[PyGreenlet_API_pointers];
GREENLET_NOINLINE_INIT();

try {
CreatedModule m(greenlet_module_def);
Expand Down
54 changes: 9 additions & 45 deletions src/greenlet/greenlet_compiler_compat.hpp
Expand Up @@ -5,11 +5,11 @@
/**
* Definitions to aid with compatibility with different compilers.
*
* .. caution:: Use extreme care with G_NOEXCEPT.
* .. caution:: Use extreme care with noexcept.
* Some compilers and runtimes, specifically gcc/libgcc/libstdc++ on
* Linux, implement stack unwinding by throwing an uncatchable
* exception, one that specifically does not appear to be an active
* exception to the rest of the runtime. If this happens while we're in a G_NOEXCEPT function,
* exception to the rest of the runtime. If this happens while we're in a noexcept function,
* we have violated our dynamic exception contract, and so the runtime
* will call std::terminate(), which kills the process with the
* unhelpful message "terminate called without an active exception".
Expand All @@ -21,7 +21,7 @@
* attempts to obtain the GIL, it notices that the interpreter is
* exiting and calls ``pthread_exit()``. This in turn starts to unwind
* the stack by throwing that exception. But we had the ``PyCall``
* functions annotated as G_NOEXCEPT, so the runtime terminated us.
* functions annotated as noexcept, so the runtime terminated us.
*
* #2 0x00007fab26fec2b7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
* #3 0x00007fab26febb3c in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6
Expand All @@ -36,34 +36,8 @@
* #13 0x00000000006101cd in socket_gethostbyname
*/


/* The compiler used for Python 2.7 on Windows doesn't include
either stdint.h or cstdint.h. Nor does it understand nullptr or have
std::shared_ptr. = delete, etc Sigh. */
#if defined(_MSC_VER) && _MSC_VER <= 1500
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
typedef unsigned int uint32_t;
// C++ defines NULL to be 0, which is ambiguous
// with an integer in certain cases, and won't autoconvert to a
// pointer in other cases.
#define nullptr NULL
#define G_HAS_METHOD_DELETE 0
// Use G_EXPLICIT_OP as the prefix for operator methods
// that should be explicit. Old MSVC doesn't support explicit operator
// methods.
#define G_EXPLICIT_OP
#define G_NOEXCEPT throw()
// This version doesn't support "objects with internal linkage"
// in non-type template arguments. Translation: function pointer
// template arguments cannot be for static functions.
#define G_FP_TMPL_STATIC
#else
// Newer, reasonable compilers implementing C++11 or so.
#include <cstdint>
#define G_HAS_METHOD_DELETE 1
#define G_EXPLICIT_OP explicit
#define G_NOEXCEPT noexcept

# if defined(__clang__)
# define G_FP_TMPL_STATIC static
# else
Expand All @@ -72,9 +46,7 @@ typedef unsigned int uint32_t;
# define G_FP_TMPL_STATIC
# endif

#endif

#if G_HAS_METHOD_DELETE == 1
# define G_NO_COPIES_OF_CLS(Cls) private: \
Cls(const Cls& other) = delete; \
Cls& operator=(const Cls& other) = delete
Expand All @@ -84,17 +56,7 @@ typedef unsigned int uint32_t;

# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \
Cls(const Cls& other) = delete;
#else
# define G_NO_COPIES_OF_CLS(Cls) private: \
Cls(const Cls& other); \
Cls& operator=(const Cls& other)

# define G_NO_ASSIGNMENT_OF_CLS(Cls) private: \
Cls& operator=(const Cls& other)

# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \
Cls(const Cls& other);
#endif

// CAUTION: MSVC is stupidly picky:
//
Expand All @@ -106,24 +68,26 @@ typedef unsigned int uint32_t;
// So pointer return types must be handled differently (because of the
// trailing *), or you get inscrutable compiler warnings like "error
// C2059: syntax error: ''"
//
// In C++ 11, there is a standard syntax for attributes, and
// GCC defines an attribute to use with this: [[gnu:noinline]].
// In the future, this is expected to become standard.

#if defined(__GNUC__) || defined(__clang__)
/* We used to check for GCC 4+ or 3.4+, but those compilers are
laughably out of date. Just assume they support it. */
# define GREENLET_NOINLINE_SUPPORTED
# define GREENLET_NOINLINE(name) __attribute__((noinline)) name
# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#elif defined(_MSC_VER)
/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */
# define GREENLET_NOINLINE_SUPPORTED
# define GREENLET_NOINLINE(name) __declspec(noinline) name
# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name
# define UNUSED(x) UNUSED_ ## x
#endif

#if defined(_MSC_VER)
# define G_NOEXCEPT_WIN32 G_NOEXCEPT
# define G_NOEXCEPT_WIN32 noexcept
#else
# define G_NOEXCEPT_WIN32
#endif
Expand Down

0 comments on commit f3574c4

Please sign in to comment.