Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions 6 Doc/c-api/sys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ Operating System Utilities

Return true when the interpreter runs out of stack space. This is a reliable
check, but is only available when :const:`USE_STACKCHECK` is defined (currently
on Windows using the Microsoft Visual C++ compiler). :const:`USE_STACKCHECK`
will be defined automatically; you should never change the definition in your
own code.
on macOS and Windows using the Microsoft Visual C++ compiler).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to check with a native speaker, but this seems to suggest that USE_STACKCHECK is only enabled on macOS when using the Microsoft compiler, which is not what you're trying to say here.

That said, I'm not a native english speaker and might interpret this sentence too strictly.

:const:`USE_STACKCHECK` will be defined automatically; you should never
change the definition in your own code.


.. c:function:: PyOS_sighandler_t PyOS_getsig(int i)
Expand Down
3 changes: 3 additions & 0 deletions 3 Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Summary -- Release highlights
New Features
============
Comment thread
corona10 marked this conversation as resolved.
Outdated

* An interpreter on macOS system now supports ``USE_STACKCHECK``. The interpreter
detects running out of stack space and raise :class:`MemoryError`.
(Contributed by Dong-hee Na in :issue:`33955`.)


Other Language Changes
Expand Down
4 changes: 4 additions & 0 deletions 4 Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ struct _ts {
/* Unique thread state id. */
uint64_t id;

#if defined(__APPLE__)
size_t stack_space;
Comment thread
corona10 marked this conversation as resolved.
Outdated
size_t last_stack_remain;
#endif
/* XXX signal handlers should also be here */

};
Expand Down
5 changes: 5 additions & 0 deletions 5 Include/pythonrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ PyAPI_DATA(PyThreadState*) _PyOS_ReadlineTState;
#define USE_STACKCHECK
#endif

#if defined(__APPLE__)
/* Enable stack checking under macOS */
#define USE_STACKCHECK
#endif

#ifdef USE_STACKCHECK
/* Check that we aren't overflowing our stack */
PyAPI_FUNC(int) PyOS_CheckStack(void);
Expand Down
12 changes: 12 additions & 0 deletions 12 Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,18 @@ def test_recursionlimit(self):
self.assertEqual(sys.getrecursionlimit(), 10000)
sys.setrecursionlimit(oldlimit)

@unittest.skipUnless(sys.platform == 'darwin', 'macOS only')
def test_setrecursion_with_memory_error(self):
oldlimit = sys.getrecursionlimit()
def f():
f()
try:
sys.setrecursionlimit(1 << 30)
with self.assertRaisesRegex(MemoryError, r'Stack overflow'):
f()
finally:
sys.setrecursionlimit(oldlimit)

def test_recursionlimit_recovery(self):
if hasattr(sys, 'gettrace') and sys.gettrace():
self.skipTest('fatal error if run with a trace function')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
A interpreter on macOS system now supports USE_STACKCHECK.
The interpreter detects running out of stack space and raise MemoryError.
5 changes: 4 additions & 1 deletion 5 Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,10 @@ new_threadstate(PyInterpreterState *interp, int init)
tstate->context_ver = 1;

tstate->id = ++interp->tstate_next_unique_id;

#if defined(__APPLE__)
tstate->stack_space = 0;
Comment thread
corona10 marked this conversation as resolved.
Outdated
tstate->last_stack_remain = 0;
#endif
if (init) {
_PyThreadState_Init(tstate);
}
Expand Down
70 changes: 70 additions & 0 deletions 70 Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,76 @@ PyOS_CheckStack(void)

#endif /* WIN32 && _MSC_VER */

#if defined(__APPLE__)

#define MAC_OS_10_9 13
#define MAC_OS_10_10 14
#define MAC_OS_10_11 15

#include <sys/utsname.h>

static size_t
Get_Stack_Check_Size(const pthread_t *thread_self)
Comment thread
corona10 marked this conversation as resolved.
Outdated
{
size_t stack_space;
if (pthread_main_np() == 1) {
// On macOS 10.09 - 10.11 pthread_get_stacksize_np
// returns wrong stack size on main thread.
// ref: https://github.com/rust-lang/rust/issues/43347#issuecomment-316783599
// ref: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8020753
struct utsname sysinfo;
uname(&sysinfo);
const int major_version = atoi(sysinfo.release);
if (major_version == MAC_OS_10_9 ||
major_version == MAC_OS_10_10 ||
major_version == MAC_OS_10_11) {
struct rlimit limit;
getrlimit(RLIMIT_STACK, &limit);
stack_space = limit.rlim_cur;
}
else {
stack_space = pthread_get_stacksize_np(*thread_self);
}
}
else {
stack_space = pthread_get_stacksize_np(*thread_self);
}
return stack_space;
}

/*
* Return non-zero when we run out of memory on the stack; zero otherwise.
*/
int
PyOS_CheckStack(void)
{
PyThreadState *tstate = _PyThreadState_GET();
if (tstate->stack_space == 0) {
const pthread_t thread_self = pthread_self();
size_t stack_space = Get_Stack_Check_Size(&thread_self);
tstate->stack_space = stack_space;
tstate->last_stack_remain = stack_space;
}
const uintptr_t end = (uintptr_t)pthread_get_stackaddr_np(pthread_self());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if it's implemented in pure userspace, or if it's a system call?

const uintptr_t frame = (uintptr_t)__builtin_frame_address(0);
Comment thread
corona10 marked this conversation as resolved.
Outdated
const size_t remains = tstate->stack_space - (end - frame);
size_t required_stack_space;

if (remains > tstate->last_stack_remain) {
required_stack_space = remains - tstate->last_stack_remain;
}
else {
required_stack_space = tstate->last_stack_remain - remains;
}

tstate->last_stack_remain = remains;
if (remains >= required_stack_space) {
return 0;
}
return 1;
}
#endif /* __APPLE__ */

/* Alternate implementations can be added here... */

#endif /* USE_STACKCHECK */
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.