Skip to content

Commit b750ff0

Browse files
committed
Allow pending call procesing to be deferred
1 parent 15a931c commit b750ff0

File tree

9 files changed

+3892
-3872
lines changed

9 files changed

+3892
-3872
lines changed

Include/opcode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ extern "C" {
128128
#define BUILD_TUPLE_UNPACK_WITH_CALL 158
129129
#define LOAD_METHOD 160
130130
#define CALL_METHOD 161
131+
#define DEFER_PENDING_UNTIL 162
131132

132133
/* EXCEPT_HANDLER is a special, implicit block type which is created when
133134
entering an except handler. It is not an opcode but we define it here

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ def _write_atomic(path, data, mode=0o666):
241241
# Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257)
242242
# Python 3.6rc1 3379 (more thorough __class__ validation #23722)
243243
# Python 3.7a0 3390 (add LOAD_METHOD and CALL_METHOD opcodes)
244+
# 3391 (add DEFER_PENDING_UNTIL opcode)
244245
#
245246
# MAGIC must change whenever the bytecode emitted by the compiler may no
246247
# longer be understood by older implementations of the eval loop (usually
@@ -249,7 +250,7 @@ def _write_atomic(path, data, mode=0o666):
249250
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
250251
# in PC/launcher.c must also be updated.
251252

252-
MAGIC_NUMBER = (3390).to_bytes(2, 'little') + b'\r\n'
253+
MAGIC_NUMBER = (3391).to_bytes(2, 'little') + b'\r\n'
253254
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
254255

255256
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,6 @@ def jabs_op(name, op):
215215
name_op('LOAD_METHOD', 160)
216216
def_op('CALL_METHOD', 161)
217217

218+
jrel_op('DEFER_PENDING_UNTIL', 162) # When to resume pending call processing
219+
218220
del def_op, name_op, jrel_op, jabs_op

Lib/test/test_with_signal_safety.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ def inject_exception(frame, event, arg):
2323
return
2424
frame.f_trace_opcodes = True
2525
if frame.f_lasti >= target_instruction:
26-
raise InjectedException(f"Failing after {target_instruction}")
26+
if frame.f_lasti > frame.f_pendingi:
27+
raise InjectedException(f"Failing after {frame.f_lasti}")
2728
return inject_exception
2829
sys.settrace(inject_exception)
2930

@@ -72,13 +73,9 @@ def traced_function():
7273
return
7374
target_instruction = -1
7475
num_instructions = len(traced_function.__code__.co_code) - 2
75-
import dis
76-
dis.dis(traced_function)
7776
while target_instruction < num_instructions:
7877
target_instruction += 1
7978
raise_after_instruction(traced_function, target_instruction)
80-
if verbose:
81-
print(f"Raising exception after {target_instruction}")
8279
try:
8380
traced_function()
8481
except InjectedException:
@@ -107,8 +104,6 @@ async def traced_coroutine():
107104
while target_instruction < num_instructions:
108105
target_instruction += 1
109106
raise_after_instruction(traced_coroutine, target_instruction)
110-
if verbose:
111-
print(f"Raising exception after {target_instruction}")
112107
try:
113108
loop.run_until_complete(traced_coroutine())
114109
except InjectedException:

Python/ceval.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
586586
int instr_ub = -1, instr_lb = 0, instr_prev = -1;
587587

588588
const _Py_CODEUNIT *first_instr;
589+
const _Py_CODEUNIT *handle_pending_after;
589590
PyObject *names;
590591
PyObject *consts;
591592

@@ -716,6 +717,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
716717
} while (0)
717718
#define JUMPTO(x) (next_instr = first_instr + (x) / sizeof(_Py_CODEUNIT))
718719
#define JUMPBY(x) (next_instr += (x) / sizeof(_Py_CODEUNIT))
720+
#define DEFERUNTIL(x) (handle_pending_after = next_instr + (x) / sizeof(_Py_CODEUNIT))
721+
#define DEFER_OFFSET() (sizeof(_Py_CODEUNIT) * (int)(handle_pending_after - first_instr))
719722

720723
/* OpCode prediction macros
721724
Some opcodes tend to come in pairs thus making it possible to
@@ -894,6 +897,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
894897
assert(PyBytes_GET_SIZE(co->co_code) % sizeof(_Py_CODEUNIT) == 0);
895898
assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(co->co_code), sizeof(_Py_CODEUNIT)));
896899
first_instr = (_Py_CODEUNIT *) PyBytes_AS_STRING(co->co_code);
900+
handle_pending_after = first_instr;
897901
/*
898902
f->f_lasti refers to the index of the last instruction,
899903
unless it's -1 in which case next_instr should be first_instr.
@@ -977,9 +981,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
977981
*/
978982
goto fast_next_opcode;
979983
}
980-
if (_Py_atomic_load_relaxed(&pendingcalls_to_do)) {
984+
if ((next_instr > handle_pending_after) &&
985+
_Py_atomic_load_relaxed(&pendingcalls_to_do)) {
981986
if (Py_MakePendingCalls() < 0)
982987
goto error;
988+
/* Allow for subsequent jumps backwards in the bytecode */
989+
handle_pending_after = first_instr;
983990
}
984991
if (_Py_atomic_load_relaxed(&gil_drop_request)) {
985992
/* Give another thread a chance */
@@ -3129,6 +3136,12 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
31293136
DISPATCH();
31303137
}
31313138

3139+
TARGET(DEFER_PENDING_UNTIL) {
3140+
DEFERUNTIL(oparg);
3141+
PREDICT(POP_BLOCK);
3142+
DISPATCH();
3143+
}
3144+
31323145
TARGET(LOAD_METHOD) {
31333146
/* Designed to work in tamdem with CALL_METHOD. */
31343147
PyObject *name = GETITEM(names, oparg);

Python/compile.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
943943
return 0;
944944
case YIELD_FROM:
945945
return -1;
946+
case DEFER_PENDING_UNTIL:
947+
return 0;
946948
case POP_BLOCK:
947949
return 0;
948950
case POP_EXCEPT:
@@ -4194,14 +4196,15 @@ expr_constant(struct compiler *c, expr_ty e)
41944196
static int
41954197
compiler_async_with(struct compiler *c, stmt_ty s, int pos)
41964198
{
4197-
basicblock *block, *finally;
4199+
basicblock *block, *finally, *finally_await;
41984200
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
41994201

42004202
assert(s->kind == AsyncWith_kind);
42014203

42024204
block = compiler_new_block(c);
42034205
finally = compiler_new_block(c);
4204-
if (!block || !finally)
4206+
finally_await = compiler_new_block(c);
4207+
if (!block || !finally || !finally_await)
42054208
return 0;
42064209

42074210
/* Evaluate EXPR */
@@ -4210,8 +4213,8 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
42104213
ADDOP(c, BEFORE_ASYNC_WITH);
42114214
ADDOP(c, GET_AWAITABLE);
42124215
ADDOP_O(c, LOAD_CONST, Py_None, consts);
4216+
ADDOP_JREL(c, DEFER_PENDING_UNTIL, block);
42134217
ADDOP(c, YIELD_FROM);
4214-
42154218
ADDOP_JREL(c, SETUP_ASYNC_WITH, finally);
42164219

42174220
/* SETUP_ASYNC_WITH pushes a finally block. */
@@ -4236,6 +4239,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
42364239
return 0;
42374240

42384241
/* End of try block; start the finally block */
4242+
ADDOP_JREL(c, DEFER_PENDING_UNTIL, finally_await);
42394243
ADDOP(c, POP_BLOCK);
42404244
compiler_pop_fblock(c, FINALLY_TRY, block);
42414245

@@ -4249,8 +4253,10 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
42494253
opcode. */
42504254
ADDOP(c, WITH_CLEANUP_START);
42514255

4256+
ADDOP_JREL(c, DEFER_PENDING_UNTIL, finally_await);
42524257
ADDOP(c, GET_AWAITABLE);
42534258
ADDOP_O(c, LOAD_CONST, Py_None, consts);
4259+
compiler_use_next_block(c, finally_await);
42544260
ADDOP(c, YIELD_FROM);
42554261

42564262
ADDOP(c, WITH_CLEANUP_FINISH);
@@ -4324,6 +4330,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
43244330
return 0;
43254331

43264332
/* End of try block; start the finally block */
4333+
ADDOP_JREL(c, DEFER_PENDING_UNTIL, finally);
43274334
ADDOP(c, POP_BLOCK);
43284335
compiler_pop_fblock(c, FINALLY_TRY, block);
43294336

0 commit comments

Comments
 (0)