]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-115999: Add free-threaded specialization for `SEND` (gh-127426)
authorNeil Schemenauer <nas-github@arctrix.com>
Tue, 3 Dec 2024 18:25:12 +0000 (10:25 -0800)
committerGitHub <noreply@github.com>
Tue, 3 Dec 2024 18:25:12 +0000 (10:25 -0800)
No additional thread safety changes are required.  Note that sending to
a generator that is shared between threads is currently not safe in the
free-threaded build.

Lib/test/test_opcache.py
Python/bytecodes.c
Python/generated_cases.c.h
Python/specialize.c

index 527114445ac13b0885bbd1732322323dde39b850..b7a18133ab8b8a7f66c2987335010e64fb7a39c5 100644 (file)
@@ -1311,6 +1311,48 @@ class TestSpecializer(TestBase):
         self.assert_specialized(contains_op_set, "CONTAINS_OP_SET")
         self.assert_no_opcode(contains_op_set, "CONTAINS_OP")
 
+    @cpython_only
+    @requires_specialization_ft
+    def test_send_with(self):
+        def run_async(coro):
+            while True:
+                try:
+                    coro.send(None)
+                except StopIteration:
+                    break
+
+        class CM:
+            async def __aenter__(self):
+                return self
+
+            async def __aexit__(self, *exc):
+                pass
+
+        async def send_with():
+            for i in range(100):
+                async with CM():
+                    x = 1
+
+        run_async(send_with())
+        # Note there are still unspecialized "SEND" opcodes in the
+        # cleanup paths of the 'with' statement.
+        self.assert_specialized(send_with, "SEND_GEN")
+
+    @cpython_only
+    @requires_specialization_ft
+    def test_send_yield_from(self):
+        def g():
+            yield None
+
+        def send_yield_from():
+            yield from g()
+
+        for i in range(100):
+            list(send_yield_from())
+
+        self.assert_specialized(send_yield_from, "SEND_GEN")
+        self.assert_no_opcode(send_yield_from, "SEND")
+
     @cpython_only
     @requires_specialization_ft
     def test_to_bool(self):
index dd28aae6a3cb6152f8beef1540958f105475266d..d6be3cebf80724bb13e57b41c8a035aa3a645364 100644 (file)
@@ -1117,7 +1117,7 @@ dummy_func(
         };
 
         specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) {
-            #if ENABLE_SPECIALIZATION
+            #if ENABLE_SPECIALIZATION_FT
             if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
                 next_instr = this_instr;
                 _Py_Specialize_Send(receiver, next_instr);
@@ -1125,7 +1125,7 @@ dummy_func(
             }
             OPCODE_DEFERRED_INC(SEND);
             ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-            #endif  /* ENABLE_SPECIALIZATION */
+            #endif  /* ENABLE_SPECIALIZATION_FT */
         }
 
         op(_SEND, (receiver, v -- receiver, retval)) {
index c31601f6d82b77a81bd628c42ffac85d99c34f51..ef191f6f697f24c28bfc02f2d6d04168e3e6988f 100644 (file)
                 receiver = stack_pointer[-2];
                 uint16_t counter = read_u16(&this_instr[1].cache);
                 (void)counter;
-                #if ENABLE_SPECIALIZATION
+                #if ENABLE_SPECIALIZATION_FT
                 if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
                     next_instr = this_instr;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 }
                 OPCODE_DEFERRED_INC(SEND);
                 ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-                #endif  /* ENABLE_SPECIALIZATION */
+                #endif  /* ENABLE_SPECIALIZATION_FT */
             }
             // _SEND
             {
index 0fe4e7904de9f8286b83eed9e7e9cc985e8f96a7..8b2d1a14c107e0eaf9cdd922315a46907a8343b2 100644 (file)
@@ -2627,28 +2627,21 @@ _Py_Specialize_Send(_PyStackRef receiver_st, _Py_CODEUNIT *instr)
 {
     PyObject *receiver = PyStackRef_AsPyObjectBorrow(receiver_st);
 
-    assert(ENABLE_SPECIALIZATION);
+    assert(ENABLE_SPECIALIZATION_FT);
     assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND);
-    _PySendCache *cache = (_PySendCache *)(instr + 1);
     PyTypeObject *tp = Py_TYPE(receiver);
     if (tp == &PyGen_Type || tp == &PyCoro_Type) {
         if (_PyInterpreterState_GET()->eval_frame) {
             SPECIALIZATION_FAIL(SEND, SPEC_FAIL_OTHER);
             goto failure;
         }
-        instr->op.code = SEND_GEN;
-        goto success;
+        specialize(instr, SEND_GEN);
+        return;
     }
     SPECIALIZATION_FAIL(SEND,
                         _PySpecialization_ClassifyIterator(receiver));
 failure:
-    STAT_INC(SEND, failure);
-    instr->op.code = SEND;
-    cache->counter = adaptive_counter_backoff(cache->counter);
-    return;
-success:
-    STAT_INC(SEND, success);
-    cache->counter = adaptive_counter_cooldown();
+    unspecialize(instr);
 }
 
 #ifdef Py_STATS