Skip to content

Commit 18618e6

Browse files
authored
bpo-35059: Add Py_STATIC_INLINE() macro (GH-10093)
* Add Py_STATIC_INLINE() macro to declare a "static inline" function. If the compiler supports it, try to always inline the function even if no optimization level was specified. * Modify pydtrace.h to use Py_STATIC_INLINE() when WITH_DTRACE is not defined. * Add an unit test on Py_DECREF() to make sure that _Py_NegativeRefcount() reports the correct filename.
1 parent d03b775 commit 18618e6

File tree

6 files changed

+94
-38
lines changed

6 files changed

+94
-38
lines changed

Include/object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,8 +729,8 @@ you can count such references to the type object.)
729729
*/
730730
#ifdef Py_REF_DEBUG
731731
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
732-
PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname,
733-
int lineno, PyObject *op);
732+
PyAPI_FUNC(void) _Py_NegativeRefcount(const char *filename, int lineno,
733+
PyObject *op);
734734
PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
735735
#define _Py_INC_REFTOTAL _Py_RefTotal++
736736
#define _Py_DEC_REFTOTAL _Py_RefTotal--

Include/pydtrace.h

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,29 @@ extern "C" {
2525

2626
/* Without DTrace, compile to nothing. */
2727

28-
static inline void PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {}
29-
static inline void PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {}
30-
static inline void PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {}
31-
static inline void PyDTrace_GC_START(int arg0) {}
32-
static inline void PyDTrace_GC_DONE(int arg0) {}
33-
static inline void PyDTrace_INSTANCE_NEW_START(int arg0) {}
34-
static inline void PyDTrace_INSTANCE_NEW_DONE(int arg0) {}
35-
static inline void PyDTrace_INSTANCE_DELETE_START(int arg0) {}
36-
static inline void PyDTrace_INSTANCE_DELETE_DONE(int arg0) {}
37-
static inline void PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {}
38-
static inline void PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {}
39-
40-
static inline int PyDTrace_LINE_ENABLED(void) { return 0; }
41-
static inline int PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; }
42-
static inline int PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; }
43-
static inline int PyDTrace_GC_START_ENABLED(void) { return 0; }
44-
static inline int PyDTrace_GC_DONE_ENABLED(void) { return 0; }
45-
static inline int PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; }
46-
static inline int PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; }
47-
static inline int PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; }
48-
static inline int PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; }
49-
static inline int PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; }
50-
static inline int PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; }
28+
Py_STATIC_INLINE(void) PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) {}
29+
Py_STATIC_INLINE(void) PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) {}
30+
Py_STATIC_INLINE(void) PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) {}
31+
Py_STATIC_INLINE(void) PyDTrace_GC_START(int arg0) {}
32+
Py_STATIC_INLINE(void) PyDTrace_GC_DONE(int arg0) {}
33+
Py_STATIC_INLINE(void) PyDTrace_INSTANCE_NEW_START(int arg0) {}
34+
Py_STATIC_INLINE(void) PyDTrace_INSTANCE_NEW_DONE(int arg0) {}
35+
Py_STATIC_INLINE(void) PyDTrace_INSTANCE_DELETE_START(int arg0) {}
36+
Py_STATIC_INLINE(void) PyDTrace_INSTANCE_DELETE_DONE(int arg0) {}
37+
Py_STATIC_INLINE(void) PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) {}
38+
Py_STATIC_INLINE(void) PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) {}
39+
40+
Py_STATIC_INLINE(int) PyDTrace_LINE_ENABLED(void) { return 0; }
41+
Py_STATIC_INLINE(int) PyDTrace_FUNCTION_ENTRY_ENABLED(void) { return 0; }
42+
Py_STATIC_INLINE(int) PyDTrace_FUNCTION_RETURN_ENABLED(void) { return 0; }
43+
Py_STATIC_INLINE(int) PyDTrace_GC_START_ENABLED(void) { return 0; }
44+
Py_STATIC_INLINE(int) PyDTrace_GC_DONE_ENABLED(void) { return 0; }
45+
Py_STATIC_INLINE(int) PyDTrace_INSTANCE_NEW_START_ENABLED(void) { return 0; }
46+
Py_STATIC_INLINE(int) PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) { return 0; }
47+
Py_STATIC_INLINE(int) PyDTrace_INSTANCE_DELETE_START_ENABLED(void) { return 0; }
48+
Py_STATIC_INLINE(int) PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) { return 0; }
49+
Py_STATIC_INLINE(int) PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) { return 0; }
50+
Py_STATIC_INLINE(int) PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) { return 0; }
5151

5252
#endif /* !WITH_DTRACE */
5353

Include/pyport.h

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -164,20 +164,37 @@ typedef int Py_ssize_clean_t;
164164
*/
165165

166166
#if defined(_MSC_VER)
167-
#if defined(PY_LOCAL_AGGRESSIVE)
168-
/* enable more aggressive optimization for visual studio */
169-
#pragma optimize("agtw", on)
170-
#endif
171-
/* ignore warnings if the compiler decides not to inline a function */
172-
#pragma warning(disable: 4710)
173-
/* fastest possible local call under MSVC */
174-
#define Py_LOCAL(type) static type __fastcall
175-
#define Py_LOCAL_INLINE(type) static __inline type __fastcall
167+
# if defined(PY_LOCAL_AGGRESSIVE)
168+
/* enable more aggressive optimization for visual studio */
169+
# pragma optimize("agtw", on)
170+
#endif
171+
/* ignore warnings if the compiler decides not to inline a function */
172+
# pragma warning(disable: 4710)
173+
/* fastest possible local call under MSVC */
174+
# define Py_LOCAL(type) static type __fastcall
175+
# define Py_LOCAL_INLINE(type) static __inline type __fastcall
176176
#else
177-
#define Py_LOCAL(type) static type
178-
#define Py_LOCAL_INLINE(type) static inline type
177+
# define Py_LOCAL(type) static type
178+
# define Py_LOCAL_INLINE(type) static inline type
179179
#endif
180180

181+
/* Declare a "static inline" function. Typical usage:
182+
183+
Py_STATIC_INLINE(int) add(int a, int b) { return a + b; }
184+
185+
If the compiler supports it, try to always inline the function even if no
186+
optimization level was specified. */
187+
#if defined(__GNUC__) || defined(__clang__)
188+
# define Py_STATIC_INLINE(TYPE) \
189+
__attribute__((always_inline)) static inline TYPE
190+
#elif defined(_MSC_VER)
191+
# define Py_STATIC_INLINE(TYPE) \
192+
static __forceinline TYPE
193+
#else
194+
# define Py_STATIC_INLINE(TYPE) static inline TYPE
195+
#endif
196+
197+
181198
/* Py_MEMCPY is kept for backwards compatibility,
182199
* see https://bugs.python.org/issue28126 */
183200
#define Py_MEMCPY memcpy

Lib/test/test_capi.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,23 @@ def items(self):
315315
self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping)
316316
self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping)
317317

318+
@unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'),
319+
'need _testcapi.negative_refcount')
320+
def test_negative_refcount(self):
321+
# bpo-35059: Check that Py_DECREF() reports the correct filename
322+
# when calling _Py_NegativeRefcount() to abort Python.
323+
code = textwrap.dedent("""
324+
import _testcapi
325+
from test import support
326+
327+
with support.SuppressCrashReport():
328+
_testcapi.negative_refcount()
329+
""")
330+
rc, out, err = assert_python_failure('-c', code)
331+
self.assertRegex(err,
332+
br'_testcapimodule\.c:[0-9]+ object at .* '
333+
br'has negative ref count', err)
334+
318335

319336
class TestPendingCalls(unittest.TestCase):
320337

Modules/_testcapimodule.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4818,6 +4818,25 @@ get_coreconfig(PyObject *self, PyObject *Py_UNUSED(args))
48184818
}
48194819

48204820

4821+
#ifdef Py_REF_DEBUG
4822+
static PyObject *
4823+
negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
4824+
{
4825+
PyObject *obj = PyUnicode_FromString("negative_refcount");
4826+
if (obj == NULL) {
4827+
return NULL;
4828+
}
4829+
assert(Py_REFCNT(obj) == 1);
4830+
4831+
Py_REFCNT(obj) = 0;
4832+
/* Py_DECREF() must call _Py_NegativeRefcount() and abort Python */
4833+
Py_DECREF(obj);
4834+
4835+
Py_RETURN_NONE;
4836+
}
4837+
#endif
4838+
4839+
48214840
static PyMethodDef TestMethods[] = {
48224841
{"raise_exception", raise_exception, METH_VARARGS},
48234842
{"raise_memoryerror", raise_memoryerror, METH_NOARGS},
@@ -5043,6 +5062,9 @@ static PyMethodDef TestMethods[] = {
50435062
{"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
50445063
{"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
50455064
{"get_coreconfig", get_coreconfig, METH_NOARGS},
5065+
#ifdef Py_REF_DEBUG
5066+
{"negative_refcount", negative_refcount, METH_NOARGS},
5067+
#endif
50465068
{NULL, NULL} /* sentinel */
50475069
};
50485070

Objects/object.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,14 @@ void dec_count(PyTypeObject *tp)
200200
#ifdef Py_REF_DEBUG
201201
/* Log a fatal error; doesn't return. */
202202
void
203-
_Py_NegativeRefcount(const char *fname, int lineno, PyObject *op)
203+
_Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
204204
{
205205
char buf[300];
206206

207207
PyOS_snprintf(buf, sizeof(buf),
208208
"%s:%i object at %p has negative ref count "
209209
"%" PY_FORMAT_SIZE_T "d",
210-
fname, lineno, op, op->ob_refcnt);
210+
filename, lineno, op, op->ob_refcnt);
211211
Py_FatalError(buf);
212212
}
213213

0 commit comments

Comments
 (0)