@@ -605,29 +605,42 @@ deque_copy_impl(dequeobject *deque)
605605 collections_state * state = find_module_state_by_def (Py_TYPE (deque ));
606606 if (Py_IS_TYPE (deque , state -> deque_type )) {
607607 dequeobject * new_deque ;
608- PyObject * rv ;
608+ Py_ssize_t n = Py_SIZE ( deque ) ;
609609
610610 new_deque = (dequeobject * )deque_new (state -> deque_type , NULL , NULL );
611611 if (new_deque == NULL )
612612 return NULL ;
613613 new_deque -> maxlen = old_deque -> maxlen ;
614- /* Fast path for the deque_repeat() common case where len(deque) == 1
615- *
616- * It's safe to not acquire the per-object lock for new_deque; it's
617- * invisible to other threads.
614+
615+ /* Copy elements directly by walking the block structure.
616+ * This is safe because the caller holds the deque lock and
617+ * the new deque is not yet visible to other threads.
618618 */
619- if (Py_SIZE (deque ) == 1 ) {
620- PyObject * item = old_deque -> leftblock -> data [old_deque -> leftindex ];
621- rv = deque_append_impl (new_deque , item );
622- } else {
623- rv = deque_extend_impl (new_deque , (PyObject * )deque );
624- }
625- if (rv != NULL ) {
626- Py_DECREF (rv );
627- return (PyObject * )new_deque ;
619+ if (n > 0 ) {
620+ block * b = old_deque -> leftblock ;
621+ Py_ssize_t index = old_deque -> leftindex ;
622+
623+ /* Space saving heuristic. Start filling from the left */
624+ assert (new_deque -> leftblock == new_deque -> rightblock );
625+ assert (new_deque -> leftindex == new_deque -> rightindex + 1 );
626+ new_deque -> leftindex = 1 ;
627+ new_deque -> rightindex = 0 ;
628+
629+ for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
630+ PyObject * item = b -> data [index ];
631+ if (deque_append_lock_held (new_deque , Py_NewRef (item ),
632+ new_deque -> maxlen ) < 0 ) {
633+ Py_DECREF (new_deque );
634+ return NULL ;
635+ }
636+ index ++ ;
637+ if (index == BLOCKLEN ) {
638+ b = b -> rightlink ;
639+ index = 0 ;
640+ }
641+ }
628642 }
629- Py_DECREF (new_deque );
630- return NULL ;
643+ return (PyObject * )new_deque ;
631644 }
632645 if (old_deque -> maxlen < 0 )
633646 result = PyObject_CallOneArg ((PyObject * )(Py_TYPE (deque )),
@@ -1983,22 +1996,6 @@ dequeiter_next(PyObject *op)
19831996 // It's safe to access it->deque without holding the per-object lock for it
19841997 // here; it->deque is only assigned during construction of it.
19851998 dequeobject * deque = it -> deque ;
1986-
1987- #ifdef Py_GIL_DISABLED
1988- // gh-144809: When called from deque_copy(), the deque is already
1989- // locked. The two-object critical section below would unlock and
1990- // re-lock the deque between calls, allowing another thread to modify
1991- // it mid-iteration. The one-object critical section avoids this
1992- // because it keeps the deque locked across calls when it's already
1993- // held, due to a fast-path optimization.
1994- if (_PyObject_IsUniquelyReferenced ((PyObject * )it )) {
1995- Py_BEGIN_CRITICAL_SECTION (deque );
1996- result = dequeiter_next_lock_held (it , deque );
1997- Py_END_CRITICAL_SECTION ();
1998- return result ;
1999- }
2000- #endif
2001-
20021999 Py_BEGIN_CRITICAL_SECTION2 (it , deque );
20032000 result = dequeiter_next_lock_held (it , deque );
20042001 Py_END_CRITICAL_SECTION2 ();
0 commit comments