@@ -631,6 +631,9 @@ typedef union {
631631 char * values ;
632632} PyDictOrValues ;
633633
634+ // Sentinel value to indicate when an update to PyDictOrValues is in-flight
635+ #define _PYDICTORVALUES_UPDATING 0x0000002
636+
634637static inline PyDictOrValues *
635638_PyObject_DictOrValuesPointer (PyObject * obj )
636639{
@@ -651,23 +654,120 @@ _PyDictOrValues_GetValues(PyDictOrValues dorv)
651654 return (PyDictValues * )(dorv .values + 1 );
652655}
653656
657+ static inline PyDictValues *
658+ _PyDictOrValues_TryGetValues (PyDictOrValues * dorv )
659+ {
660+ #ifdef Py_GIL_DISABLED
661+ char * values ;
662+ while (1 ) {
663+ values = _Py_atomic_load_ptr_acquire (& dorv -> values );
664+ if (values != (char * )_PYDICTORVALUES_UPDATING ) {
665+ if ((uintptr_t )values & 1 ) {
666+ return (PyDictValues * )(values + 1 );
667+ }
668+ // The values have become a dict or is not yet initialized
669+ return NULL ;
670+ }
671+ // There is an atomic update of the values in progress...
672+ _Py_yield ();
673+ }
674+ #else
675+ if (_PyDictOrValues_IsValues (* dorv )) {
676+ return _PyDictOrValues_GetValues (* dorv );
677+ }
678+ return NULL ;
679+ #endif
680+ }
681+
654682static inline PyObject *
655683_PyDictOrValues_GetDict (PyDictOrValues dorv )
656684{
657685 assert (!_PyDictOrValues_IsValues (dorv ));
658686 return dorv .dict ;
659687}
660688
689+ // Trys to get the dict from the PyDictOrValues and returns
690+ // a new strong reference if successful.
691+ static inline PyObject *
692+ _PyDictOrValues_TryGetDict (PyDictOrValues * dorv )
693+ {
694+ #ifdef Py_GIL_DISABLED
695+ PyObject * dict ;
696+ while (1 ) {
697+ dict = _Py_atomic_load_ptr_acquire (& dorv -> dict );
698+ if (dict != (PyObject * )_PYDICTORVALUES_UPDATING ) {
699+ if ((uintptr_t )dict & 1 ) {
700+ // The dict has become a values
701+ return NULL ;
702+ } else if (dict != NULL ) {
703+ Py_INCREF (dict );
704+ if (_Py_atomic_load_ptr_acquire (& dorv -> dict ) == dict ) {
705+ return dict ;
706+ }
707+
708+ // We've lost a race (presumably with dematerialization) so
709+ // we'll try again...
710+ Py_DECREF (dict );
711+ } else {
712+ // The dict is not yet initialized...
713+ return dict ;
714+ }
715+ }
716+ // There is an atomic update of the values in progress...
717+ _Py_yield ();
718+ }
719+ #else
720+ if (!_PyDictOrValues_IsValues (* dorv )) {
721+ Py_XINCREF (dorv -> dict );
722+ return dorv -> dict ;
723+ }
724+ return NULL ;
725+ #endif
726+ }
727+
728+ static inline bool
729+ _PyDictOrValues_TrySetDict (PyDictOrValues * dorv_ptr , void * expected , PyObject * dict )
730+ {
731+ #ifdef Py_GIL_DISABLED
732+ if (!_Py_atomic_compare_exchange_ptr (& dorv_ptr -> dict , & expected , dict )) {
733+ return false;
734+ }
735+ return true;
736+ #else
737+ dorv_ptr -> dict = dict ;
738+ return true;
739+ #endif
740+ }
741+
742+
743+
744+ static inline void
745+ _PyDictOrValues_FreeValues (PyDictValues * values )
746+ {
747+ // TODO: Maybe free with qsbr
748+ int prefix_size = ((uint8_t * )values )[-1 ];
749+ PyMem_Free (((char * )values )- prefix_size );
750+ }
751+
661752static inline void
662753_PyDictOrValues_SetValues (PyDictOrValues * ptr , PyDictValues * values )
663754{
755+ #ifdef Py_GIL_DISABLED
756+ char * expected = NULL ;
757+ if (!_Py_atomic_compare_exchange_ptr (& ptr -> values , & expected , ((char * )values ) - 1 )) {
758+ _PyDictOrValues_FreeValues (values );
759+ }
760+ #else
664761 ptr -> values = ((char * )values ) - 1 ;
762+ #endif
665763}
666764
667765extern PyObject * * _PyObject_ComputedDictPointer (PyObject * );
668766extern void _PyObject_FreeInstanceAttributes (PyObject * obj );
669767extern int _PyObject_IsInstanceDictEmpty (PyObject * );
670768
769+ extern int _PyObject_SetDict (PyObject * obj , PyObject * new_dict );
770+
671771// Export for 'math' shared extension
672772PyAPI_FUNC (PyObject * ) _PyObject_LookupSpecial (PyObject * , PyObject * );
673773
0 commit comments