Skip to content
Merged
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
15 changes: 9 additions & 6 deletions Include/internal/pycore_backoff.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern "C" {
#include <assert.h>
#include <stdbool.h>
#include "pycore_structs.h" // _Py_BackoffCounter
#include "pycore_tstate.h" // _PyPolicy

/* 16-bit countdown counters using exponential backoff.

Expand Down Expand Up @@ -127,10 +128,11 @@ trigger_backoff_counter(void)
#define JUMP_BACKWARD_INITIAL_VALUE 4000
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
static inline _Py_BackoffCounter
initial_jump_backoff_counter(void)
initial_jump_backoff_counter(_PyPolicy *policy)
{
return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE,
JUMP_BACKWARD_INITIAL_BACKOFF);
return make_backoff_counter(
policy->interp.jump_backward_initial_value,
policy->interp.jump_backward_initial_backoff);
}

/* Initial exit temperature.
Expand All @@ -141,10 +143,11 @@ initial_jump_backoff_counter(void)
#define SIDE_EXIT_INITIAL_BACKOFF 6

static inline _Py_BackoffCounter
initial_temperature_backoff_counter(void)
initial_temperature_backoff_counter(_PyPolicy *policy)
{
return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE,
SIDE_EXIT_INITIAL_BACKOFF);
return make_backoff_counter(
policy->jit.side_exit_initial_value,
policy->jit.side_exit_initial_backoff);
}

/* Unreachable backoff counter. */
Expand Down
17 changes: 17 additions & 0 deletions Include/internal/pycore_tstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,24 @@ typedef struct _PyJitTracerState {
_PyJitTracerInitialState initial_state;
_PyJitTracerPreviousState prev_state;
} _PyJitTracerState;

#endif

typedef struct _PyJitPolicy {
uint16_t side_exit_initial_value;
uint16_t side_exit_initial_backoff;
} _PyJitPolicy;

typedef struct _PyInterpreterPolicy {
uint16_t jump_backward_initial_value;
uint16_t jump_backward_initial_backoff;
} _PyInterpreterPolicy;

typedef struct _PyPolicy {
_PyJitPolicy jit;
_PyInterpreterPolicy interp;
} _PyPolicy;

// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
// PyThreadState fields are exposed as part of the C API, although most fields
// are intended to be private. The _PyThreadStateImpl fields not exposed.
Expand Down Expand Up @@ -132,6 +148,7 @@ typedef struct _PyThreadStateImpl {
#if _Py_TIER2
_PyJitTracerState jit_tracer_state;
#endif
_PyPolicy policy;
} _PyThreadStateImpl;

#ifdef __cplusplus
Expand Down
10 changes: 10 additions & 0 deletions Lib/_pyrepl/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,16 @@ def suspend(self) -> SimpleContextManager:
setattr(self, arg, prev_state[arg])
self.prepare()

@contextmanager
def suspend_colorization(self) -> SimpleContextManager:
try:
old_can_colorize = self.can_colorize
self.can_colorize = False
yield
finally:
self.can_colorize = old_can_colorize


def finish(self) -> None:
"""Called when a command signals that we're finished."""
pass
Expand Down
2 changes: 1 addition & 1 deletion Lib/_pyrepl/simple_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def maybe_run_command(statement: str) -> bool:
command = REPL_COMMANDS[statement]
if callable(command):
# Make sure that history does not change because of commands
with reader.suspend_history():
with reader.suspend_history(), reader.suspend_colorization():
command()
return True
return False
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ def f(mutex):

# PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently)
# exposed at the Python level. This test relies on ctypes to get at it.
@cpython_only
def test_PyThreadState_SetAsyncExc(self):
ctypes = import_module("ctypes")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The interactive help mode in the :term:`REPL` no longer incorrectly syntax
highlights text input as Python code. Contributed by Olga Matoula.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Factor out tracing and optimization heuristics into a single object.
Patch by Donghee Na.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix endless retry loop in :mod:`profiling.sampling` blocking mode when
threads cannot be seized due to ``EPERM``. Such threads are now skipped
instead of causing repeated error messages. Patch by Pablo Galindo.
10 changes: 8 additions & 2 deletions Modules/_remote_debugging/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -632,15 +632,21 @@ seize_thread(pid_t tid)
if (errno == ESRCH) {
return 1; // Thread gone, skip
}
if (errno == EPERM) {
// Thread may have exited, be in a special state, or already be traced.
// Skip rather than fail - this avoids endless retry loops when
// threads transiently become inaccessible.
return 1;
}
if (errno == EINVAL || errno == EIO) {
// Fallback for older kernels
if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) == 0) {
int status;
waitpid(tid, &status, __WALL);
return 0;
}
if (errno == ESRCH) {
return 1; // Thread gone
if (errno == ESRCH || errno == EPERM) {
return 1; // Thread gone or inaccessible
}
}
return -1; // Real error
Expand Down
4 changes: 2 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter);
}
else {
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter();
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy);
}
}
else {
Expand All @@ -1483,7 +1483,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
exit->temperature = restart_backoff_counter(exit->temperature);
}
else {
exit->temperature = initial_temperature_backoff_counter();
exit->temperature = initial_temperature_backoff_counter(&_tstate->policy);
}
}
_PyJit_FinalizeTracing(tstate);
Expand Down
9 changes: 5 additions & 4 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
}

static _PyExecutorObject *
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, int chain_depth);
make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies);

static int
uop_optimize(_PyInterpreterFrame *frame, PyThreadState *tstate,
Expand Down Expand Up @@ -1328,7 +1328,7 @@ sanity_check(_PyExecutorObject *executor)
* and not a NOP.
*/
static _PyExecutorObject *
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, int chain_depth)
make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies)
{
int exit_count = count_exits(buffer, length);
_PyExecutorObject *executor = allocate_executor(exit_count, length);
Expand All @@ -1337,12 +1337,13 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
}

/* Initialize exits */
int chain_depth = tstate->jit_tracer_state.initial_state.chain_depth;
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
cold->vm_data.chain_depth = chain_depth;
for (int i = 0; i < exit_count; i++) {
executor->exits[i].index = i;
executor->exits[i].temperature = initial_temperature_backoff_counter();
executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy);
}
int next_exit = exit_count-1;
_PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];
Expand Down Expand Up @@ -1510,7 +1511,7 @@ uop_optimize(
length = prepare_for_execution(buffer, length);
assert(length <= UOP_MAX_TRACE_LENGTH);
_PyExecutorObject *executor = make_executor_from_uops(
buffer, length, dependencies, _tstate->jit_tracer_state.initial_state.chain_depth);
_tstate, buffer, length, dependencies);
if (executor == NULL) {
return -1;
}
Expand Down
30 changes: 29 additions & 1 deletion Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_audit.h" // _Py_AuditHookEntry
#include "pycore_backoff.h" // JUMP_BACKWARD_INITIAL_VALUE, SIDE_EXIT_INITIAL_VALUE
#include "pycore_ceval.h" // _PyEval_AcquireLock()
#include "pycore_codecs.h" // _PyCodec_Fini()
#include "pycore_critical_section.h" // _PyCriticalSection_Resume()
Expand Down Expand Up @@ -1438,6 +1439,20 @@ decref_threadstate(_PyThreadStateImpl *tstate)
}
}

static inline void
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
long min_value, long max_value)
{
*target = default_value;
char *env = Py_GETENV(env_name);
if (env && *env != '\0') {
long value = atol(env);
if (value >= min_value && value <= max_value) {
*target = (uint16_t)value;
}
}
}

/* Get the thread state to a minimal consistent state.
Further init happens in pylifecycle.c before it can be used.
All fields not initialized here are expected to be zeroed out,
Expand Down Expand Up @@ -1523,8 +1538,21 @@ init_threadstate(_PyThreadStateImpl *_tstate,

_tstate->asyncio_running_loop = NULL;
_tstate->asyncio_running_task = NULL;

// Initialize interpreter policy from environment variables
init_policy(&_tstate->policy.interp.jump_backward_initial_value,
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE);
init_policy(&_tstate->policy.interp.jump_backward_initial_backoff,
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
#ifdef _Py_TIER2
// Initialize JIT policy from environment variables
init_policy(&_tstate->policy.jit.side_exit_initial_value,
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE);
init_policy(&_tstate->policy.jit.side_exit_initial_backoff,
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
_tstate->jit_tracer_state.code_buffer = NULL;
#endif
tstate->delete_later = NULL;
Expand Down
4 changes: 3 additions & 1 deletion Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
#if ENABLE_SPECIALIZATION_FT
_Py_BackoffCounter jump_counter, adaptive_counter;
if (enable_counters) {
jump_counter = initial_jump_backoff_counter();
PyThreadState *tstate = _PyThreadState_GET();
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
jump_counter = initial_jump_backoff_counter(&tstate_impl->policy);
adaptive_counter = adaptive_counter_warmup();
}
else {
Expand Down
Loading