Skip to content

Commit 71ba39d

Browse files
authored
gh-159603: Use pthread_attr_get_np() on FreeBSD (#159604)
Use pthread_attr_get_np() on FreeBSD to get the stack size (and guard size) of the current thread. Check if pthread_attr_get_np() function and <pthread_np.h> header are available in configure. Update test_json.test_recursion. On FreeBSD 16 with a default stack size of 512 MB, the json C implementation no longer raises RecursionError in test_highly_nested_objects_encoding(). Reduce also max_depth from 5,000 to 2,000 so test_highly_nested_objects_encoding() of the Python implementation only takes 1.6 seconds, instead of 12 seconds.
1 parent 6044c62 commit 71ba39d

7 files changed

Lines changed: 95 additions & 11 deletions

File tree

‎Lib/test/test_json/test_recursion.py‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,35 @@ def test_highly_nested_objects_decoding(self):
9191
@support.skip_emscripten_stack_overflow()
9292
@support.requires_resource('cpu')
9393
def test_highly_nested_objects_encoding(self):
94-
# See #12051
94+
# See issue gh-56260
95+
96+
# With max_depth=2_000 on Linux, the Python implementation takes around
97+
# 2 seconds to raise RecursionError, whereas the C implementation only
98+
# takes around 160 ms.
99+
max_depth = 2_000
95100
l, d = [], {}
96-
for x in range(500_000):
101+
for x in range(max_depth * 10):
97102
l, d = [l], {'k':d}
98-
with self.assertRaises(RecursionError):
99-
with support.infinite_recursion(5000):
103+
104+
# Don't use assertRaises(RecursionError): on FreeBSD 16 with a default
105+
# stack size of 512 MiB, the C implementation doesn't raise
106+
# RecursionError.
107+
try:
108+
with support.infinite_recursion(max_depth):
100109
self.dumps(l)
101-
with self.assertRaises(RecursionError):
102-
with support.infinite_recursion(5000):
110+
except RecursionError:
111+
pass
112+
try:
113+
with support.infinite_recursion(max_depth):
103114
self.dumps(d)
115+
except RecursionError:
116+
pass
104117

105118
@support.skip_if_huge_c_stack()
106119
@support.skip_emscripten_stack_overflow()
107120
@support.skip_wasi_stack_overflow()
108121
def test_endless_recursion(self):
109-
# See #12051
122+
# See issue gh-56260
110123
class EndlessJSONEncoder(self.json.JSONEncoder):
111124
def default(self, o):
112125
"""If check_circular is False, this will keep adding another list."""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
On FreeBSD, call ``pthread_attr_get_np()`` to get the stack size (and guard
2+
size) of the current thread. Patch by Victor Stinner.

‎Python/ceval.c‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
#include "ceval.h"
44
#include "pycore_long.h"
55

6+
#ifdef HAVE_PTHREAD_NP_H
7+
# include <pthread_np.h> // pthread_attr_get_np()
8+
#endif
9+
10+
611
int
712
Py_GetRecursionLimit(void)
813
{
@@ -147,13 +152,18 @@ hardware_stack_limits(uintptr_t *base, uintptr_t *top, uintptr_t sp)
147152
// limits compared to the old stack size estimation. musl doesn't give
148153
// us a way to detect it, so we have to assume any non-glibc,
149154
// non-Android Linux is musl and mustn't rely on pthread_getattr_np.
150-
# if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && \
151-
!defined(__NetBSD__) && \
152-
(defined(__GLIBC__) || defined(__ANDROID__) || !defined(__linux__))
155+
# if (defined(HAVE_PTHREAD_GETATTR_NP) || defined(_Py_HAVE_PTHREAD_ATTR_GET_NP)) \
156+
&& !defined(_AIX) && !defined(__NetBSD__) \
157+
&& (defined(__GLIBC__) || defined(__ANDROID__) || !defined(__linux__))
153158
size_t stack_size, guard_size;
154159
void *stack_addr;
155160
pthread_attr_t attr;
161+
#ifdef _Py_HAVE_PTHREAD_ATTR_GET_NP
162+
int err = pthread_attr_init(&attr);
163+
err |= pthread_attr_get_np(pthread_self(), &attr);
164+
#else
156165
int err = pthread_getattr_np(pthread_self(), &attr);
166+
#endif
157167
if (err == 0) {
158168
err = pthread_attr_getguardsize(&attr, &guard_size);
159169
err |= pthread_attr_getstack(&attr, &stack_addr, &stack_size);

‎Tools/build/check_capi_macros_ignored.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ HAVE_PTHREAD_GET_NAME_NP
433433
HAVE_PTHREAD_H
434434
HAVE_PTHREAD_INIT
435435
HAVE_PTHREAD_KILL
436+
HAVE_PTHREAD_NP_H
436437
HAVE_PTHREAD_SETNAME_NP
437438
HAVE_PTHREAD_SET_NAME_NP
438439
HAVE_PTHREAD_SIGMASK

‎configure‎

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎configure.ac‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3195,7 +3195,8 @@ AC_CHECK_HEADERS([ \
31953195
alloca.h asm/types.h bluetooth.h conio.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \
31963196
io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/fs.h linux/limits.h linux/memfd.h \
31973197
linux/netfilter_ipv4.h linux/random.h linux/soundcard.h linux/sched.h \
3198-
linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \
3198+
linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h \
3199+
poll.h process.h pthread.h pthread_np.h pty.h \
31993200
sched.h setjmp.h shadow.h signal.h spawn.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \
32003201
sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \
32013202
sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/pidfd.h sys/poll.h \
@@ -5586,6 +5587,8 @@ AC_CHECK_FUNCS([ \
55865587

55875588
PY_CHECK_FUNC_PRIVATE([freadlink], [@%:@include <unistd.h>])
55885589
PY_CHECK_FUNC_PRIVATE([strerror_r], [@%:@include <string.h>])
5590+
# FreeBSD pthread_attr_get_np()
5591+
PY_CHECK_FUNC_PRIVATE([pthread_attr_get_np], [@%:@include <pthread_np.h>])
55895592

55905593

55915594
# os.statx uses Linux's statx function. AIX also has a function named statx,

‎pyconfig.h.in‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,9 @@
11381138
/* Define to 1 if you have the 'pthread_kill' function. */
11391139
#undef HAVE_PTHREAD_KILL
11401140

1141+
/* Define to 1 if you have the <pthread_np.h> header file. */
1142+
#undef HAVE_PTHREAD_NP_H
1143+
11411144
/* Define to 1 if you have the 'pthread_setname_np' function. */
11421145
#undef HAVE_PTHREAD_SETNAME_NP
11431146

@@ -2214,6 +2217,9 @@
22142217
/* Define if you have the 'PR_SET_VMA_ANON_NAME' constant. */
22152218
#undef _Py_HAVE_PR_SET_VMA_ANON_NAME
22162219

2220+
/* Define if you have the 'pthread_attr_get_np' function. */
2221+
#undef _Py_HAVE_PTHREAD_ATTR_GET_NP
2222+
22172223
/* Define if you have the 'sinpi' function. */
22182224
#undef _Py_HAVE_SINPI
22192225

0 commit comments

Comments
 (0)