From 99e0367490d524b0283e35b06c08c8c87b3dba9f Mon Sep 17 00:00:00 2001 From: GrieferAtWork Date: Sun, 8 Dec 2024 18:13:36 +0100 Subject: [PATCH] Auto-patch sub-classes with optimized seq/map getsets at runtime --- include/deemon/mro.h | 81 ++-- src/deemon/objects/seq/default-api-methods.c | 152 +++++++- src/deemon/runtime/mro-impl-cache.c.inl | 4 +- src/deemon/runtime/mro.c | 377 ++++++++++++++++--- 4 files changed, 521 insertions(+), 93 deletions(-) diff --git a/include/deemon/mro.h b/include/deemon/mro.h index 85bdec65d..b2493dcad 100644 --- a/include/deemon/mro.h +++ b/include/deemon/mro.h @@ -211,32 +211,36 @@ INTDEF NONNULL((1)) void DCALL Dee_membercache_fini(struct Dee_membercache *__re /* Try to insert a new caching point into the given Dee_membercache `self'. * @param: self: The cache to insert into. - * @param: decl: The type providing the declaration. */ -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addmethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addgetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addmember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancemember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); -INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstanceattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib); + * @param: decl: The type providing the declaration. + * @return: 2: Slot is being initialized by a different thread (not added) + * @return: 1: Slot already exists in cache (not added) + * @return: 0: Success + * @return: -1: OOM (NO error was thrown) */ +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addgetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); +INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstanceattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib); #ifdef __INTELLISENSE__ /* Cache an instance member (e.g. `tp_methods') in `tp_cache'. */ -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); /* Cache a class member (e.g. `tp_class_methods') in `tp_class_cache'. */ -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); /* Cache an instance member (e.g. `tp_methods') in `tp_class_cache'. */ -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); -INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member); +INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib); #else /* __INTELLISENSE__ */ #define DeeType_CacheMethod(self, decl, hash, method) Dee_membercache_addmethod(&(self)->tp_cache, decl, hash, method) #define DeeType_CacheGetSet(self, decl, hash, getset) Dee_membercache_addgetset(&(self)->tp_cache, decl, hash, getset) @@ -1732,6 +1736,41 @@ DFUNDEF WUNUSED NONNULL((1, 2)) bool DCALL Dee_type_member_bound(struct Dee_type DFUNDEF WUNUSED NONNULL((1, 2, 3)) int DCALL Dee_type_member_set(struct Dee_type_member const *desc, DeeObject *self, DeeObject *value); #define Dee_type_member_del(desc, self) Dee_type_member_set(desc, self, Dee_None) + +/* Try to add the specified attribute to the cache of "self". + * - If this fails due to OOM, return `-1', but DON'T throw an exception + * If the MRO cache already contains an entry for the named attribute: + * - Verify that the existing entry is for the same type of attribute (`MEMBERCACHE_*'), + * such that it can be patched without having to alter `mcs_type' (since having to do + * so would result in a non-resolvable race condition where another thread is currently + * dereferencing the function pointers from the existing entry). + * If this verification fails, return `1'. + * - For `DeeTypeMRO_Patch*Method', it is also verified that both the + * old and new function pointers share a common `TYPE_METHOD_FKWDS'. + * - If the type matches, the pre-existing cache entries pointers are patched such that + * they will now reference those from the given parameters. + * Note that for this purpose, this exchange is atomic for each individual function + * pointer (but not all pointers as a whole) -- in the case of `DeeTypeMRO_Patch*GetSet', + * another thread may invoke (e.g.) an out-dated `gs_del' after `gs_get' was already + * patched. + * + * NOTE: Generally, only use these functions for self-optimizing methods in base-classes + * that wish to skip certain type-dependent verification steps during future calls. + * (e.g. `Sequence.first', `Mapping.keys') + * + * @param: old_*: [0..1] When non-NULL, use these values for compare-exchange operations. + * But also note that when there are many function pointers, some may + * be set, while others cannot be -- here, an attempt to exchange + * pointers is made for *all* pointers, and success is indicated if + * at least 1 pointer could be exchanged. + * @return: 1: Failure (cache entry cannot be patched like this) + * @return: 0: Success + * @return: -1: Patching failed due to OOM (but no error was thrown!) */ +DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *new_method, /*0..1*/ struct type_method const *old_method); +DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset); +DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *new_method, /*0..1*/ struct type_method const *old_method); +DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset); + DECL_END #endif /* !GUARD_DEEMON_MRO_H */ diff --git a/src/deemon/objects/seq/default-api-methods.c b/src/deemon/objects/seq/default-api-methods.c index f9463ca0e..eec9daac0 100644 --- a/src/deemon/objects/seq/default-api-methods.c +++ b/src/deemon/objects/seq/default-api-methods.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -6213,57 +6214,182 @@ DeeMap_DefaultIterValuesWithError(DeeObject *self) { /* Deemon user-code wrappers */ /************************************************************************/ +/*[[[deemon +import define_Dee_HashStr from rt.gen.hash; +print define_Dee_HashStr("first"); +print define_Dee_HashStr("last"); +print define_Dee_HashStr("keys"); +print define_Dee_HashStr("values"); +print define_Dee_HashStr("iterkeys"); +print define_Dee_HashStr("itervalues"); +print define_Dee_HashStr("cb"); +print define_Dee_HashStr("start"); +print define_Dee_HashStr("end"); +]]]*/ +#define Dee_HashStr__first _Dee_HashSelectC(0xa9f0e818, 0x9d12a485470a29a7) +#define Dee_HashStr__last _Dee_HashSelectC(0x185a4f9a, 0x760894ca6d41e4dc) +#define Dee_HashStr__keys _Dee_HashSelectC(0x97e36be1, 0x654d31bc4825131c) +#define Dee_HashStr__values _Dee_HashSelectC(0x33b551c8, 0xf6e3e991b86d1574) +#define Dee_HashStr__iterkeys _Dee_HashSelectC(0x62bd6adc, 0x535ac8ab28094ab3) +#define Dee_HashStr__itervalues _Dee_HashSelectC(0xcb00bab3, 0xe9a89082a994930a) +#define Dee_HashStr__cb _Dee_HashSelectC(0x75ffadba, 0x2501dbb50208b92e) +#define Dee_HashStr__start _Dee_HashSelectC(0xa2ed6890, 0x80b621ce3c3982d5) +#define Dee_HashStr__end _Dee_HashSelectC(0x37fb4a05, 0x6de935c204dc3d01) +/*[[[end]]]*/ + + +/* Helper functions that (ab-)use the attribute cache system of types + * to inject the most optimized version of getsets into top-level objects: + * - Sequence.first + * - Sequence.last + * - Mapping.keys + * - Mapping.values + * - Mapping.iterkeys + * - Mapping.itervalues + */ +#ifdef __OPTIMIZE_SIZE__ +#define maybe_cache_optimized_seq_first_in_membercache(self) (void)0 +#define maybe_cache_optimized_seq_last_in_membercache(self) (void)0 +#define maybe_cache_optimized_map_keys_in_membercache(self) (void)0 +#define maybe_cache_optimized_map_values_in_membercache(self) (void)0 +#define maybe_cache_optimized_map_iterkeys_in_membercache(self) (void)0 +#define maybe_cache_optimized_map_itervalues_in_membercache(self) (void)0 +#else /* __OPTIMIZE_SIZE__ */ +PRIVATE struct type_getset tpconst gs_default_seq_first = +TYPE_GETSET_BOUND_NODOC(NULL, &default_seq_getfirst, &default_seq_delfirst, + &default_seq_setfirst, &default_seq_boundfirst); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_seq_first_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_first; + new_getset.gs_get = DeeType_RequireSeqGetFirst(self); + new_getset.gs_del = DeeType_RequireSeqDelFirst(self); + new_getset.gs_set = DeeType_RequireSeqSetFirst(self); + new_getset.gs_bound = DeeType_RequireSeqBoundFirst(self); + DeeTypeMRO_PatchGetSet(self, &DeeSeq_Type, Dee_HashStr__first, + &new_getset, &gs_default_seq_first); +} + +PRIVATE struct type_getset tpconst gs_default_seq_last = +TYPE_GETSET_BOUND_NODOC(NULL, &default_seq_getlast, &default_seq_dellast, + &default_seq_setlast, &default_seq_boundlast); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_seq_last_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_last; + new_getset.gs_get = DeeType_RequireSeqGetLast(self); + new_getset.gs_del = DeeType_RequireSeqDelLast(self); + new_getset.gs_set = DeeType_RequireSeqSetLast(self); + new_getset.gs_bound = DeeType_RequireSeqBoundLast(self); + DeeTypeMRO_PatchGetSet(self, &DeeSeq_Type, Dee_HashStr__last, + &new_getset, &gs_default_seq_last); +} + +PRIVATE struct type_getset tpconst gs_default_map_keys = +TYPE_GETTER_NODOC(NULL, &default_map_keys); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_map_keys_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_keys; + new_getset.gs_get = DeeType_RequireMapKeys(self); + new_getset.gs_del = NULL; + new_getset.gs_set = NULL; + new_getset.gs_bound = NULL; + DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__keys, + &new_getset, &gs_default_map_keys); +} + +PRIVATE struct type_getset tpconst gs_default_map_values = +TYPE_GETTER_NODOC(NULL, &default_map_values); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_map_values_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_values; + new_getset.gs_get = DeeType_RequireMapValues(self); + new_getset.gs_del = NULL; + new_getset.gs_set = NULL; + new_getset.gs_bound = NULL; + DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__values, + &new_getset, &gs_default_map_values); +} + +PRIVATE struct type_getset tpconst gs_default_map_iterkeys = +TYPE_GETTER_NODOC(NULL, &default_map_iterkeys); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_map_iterkeys_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_iterkeys; + new_getset.gs_get = DeeType_RequireMapIterKeys(self); + new_getset.gs_del = NULL; + new_getset.gs_set = NULL; + new_getset.gs_bound = NULL; + DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__iterkeys, + &new_getset, &gs_default_map_iterkeys); +} + +PRIVATE struct type_getset tpconst gs_default_map_itervalues = +TYPE_GETTER_NODOC(NULL, &default_map_itervalues); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_map_itervalues_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_itervalues; + new_getset.gs_get = DeeType_RequireMapIterValues(self); + new_getset.gs_del = NULL; + new_getset.gs_set = NULL; + new_getset.gs_bound = NULL; + DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__itervalues, + &new_getset, &gs_default_map_itervalues); +} +#endif /* !__OPTIMIZE_SIZE__ */ + INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL default_seq_getfirst(DeeObject *__restrict self) { + maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeGetFirst(self); } INTERN WUNUSED NONNULL((1)) int DCALL default_seq_boundfirst(DeeObject *__restrict self) { + maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeBoundFirst(self); } INTERN WUNUSED NONNULL((1)) int DCALL default_seq_delfirst(DeeObject *__restrict self) { + maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeDelFirst(self); } INTERN WUNUSED NONNULL((1, 2)) int DCALL default_seq_setfirst(DeeObject *self, DeeObject *value) { + maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeSetFirst(self, value); } INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL default_seq_getlast(DeeObject *__restrict self) { + maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeGetLast(self); } INTERN WUNUSED NONNULL((1)) int DCALL default_seq_boundlast(DeeObject *__restrict self) { + maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeBoundLast(self); } INTERN WUNUSED NONNULL((1)) int DCALL default_seq_dellast(DeeObject *__restrict self) { + maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeDelLast(self); } INTERN WUNUSED NONNULL((1, 2)) int DCALL default_seq_setlast(DeeObject *self, DeeObject *value) { + maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self)); return DeeSeq_InvokeSetLast(self, value); } -/*[[[deemon -import define_Dee_HashStr from rt.gen.hash; -print define_Dee_HashStr("cb"); -print define_Dee_HashStr("start"); -print define_Dee_HashStr("end"); -]]]*/ -#define Dee_HashStr__cb _Dee_HashSelectC(0x75ffadba, 0x2501dbb50208b92e) -#define Dee_HashStr__start _Dee_HashSelectC(0xa2ed6890, 0x80b621ce3c3982d5) -#define Dee_HashStr__end _Dee_HashSelectC(0x37fb4a05, 0x6de935c204dc3d01) -/*[[[end]]]*/ - PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) DREF DeeObject *DCALL do_seq_enumerate_with_kw(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw) { @@ -7366,21 +7492,25 @@ DeeMH_map_popitem(DeeObject *self, size_t argc, DeeObject *const *argv) { INTERN WUNUSED NONNULL((1)) DREF DeeObject * (DCALL default_map_keys)(DeeObject *self) { + maybe_cache_optimized_map_keys_in_membercache(Dee_TYPE(self)); return DeeMap_InvokeKeys(self); } INTERN WUNUSED NONNULL((1)) DREF DeeObject * (DCALL default_map_values)(DeeObject *self) { + maybe_cache_optimized_map_values_in_membercache(Dee_TYPE(self)); return DeeMap_InvokeValues(self); } INTERN WUNUSED NONNULL((1)) DREF DeeObject * (DCALL default_map_iterkeys)(DeeObject *self) { + maybe_cache_optimized_map_iterkeys_in_membercache(Dee_TYPE(self)); return DeeMap_InvokeIterKeys(self); } INTERN WUNUSED NONNULL((1)) DREF DeeObject * (DCALL default_map_itervalues)(DeeObject *self) { + maybe_cache_optimized_map_itervalues_in_membercache(Dee_TYPE(self)); return DeeMap_InvokeIterValues(self); } diff --git a/src/deemon/runtime/mro-impl-cache.c.inl b/src/deemon/runtime/mro-impl-cache.c.inl index 1624a2b3c..679c769a1 100644 --- a/src/deemon/runtime/mro-impl-cache.c.inl +++ b/src/deemon/runtime/mro-impl-cache.c.inl @@ -906,7 +906,7 @@ INTERN WUNUSED LOCAL_ATTR_NONNULL int #ifdef LOCAL_HAS_len size_t attrlen, #endif /* LOCAL_HAS_len */ - dhash_t hash + Dee_hash_t hash #if defined(LOCAL_IS_SET) || defined(LOCAL_IS_SET_BASIC) , DeeObject *value #elif defined(LOCAL_IS_CALL) || defined(LOCAL_IS_CALL_KW) @@ -974,7 +974,7 @@ INTERN WUNUSED LOCAL_ATTR_NONNULL int #endif /* ... */ DREF struct Dee_membercache_table *table; - dhash_t i, perturb; + Dee_hash_t i, perturb; if unlikely(!Dee_membercache_acquiretable(&tp_self->LOCAL_tp_cache, &table)) goto cache_miss; perturb = i = Dee_membercache_table_hashst(table, LOCAL_hash); diff --git a/src/deemon/runtime/mro.c b/src/deemon/runtime/mro.c index 45830ddef..ecdd18a4f 100644 --- a/src/deemon/runtime/mro.c +++ b/src/deemon/runtime/mro.c @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -221,9 +222,11 @@ Dee_membercache_table_new(struct Dee_membercache_table const *old_table, /* Try to add a slot to the given member-cache table. - * @return: true: Success - * @return: false: No more free slots (caller must allocate a new member-cache table) */ -PRIVATE NONNULL((1, 2)) bool DCALL + * @return: 2: Slot is being initialized by a different thread (not added) + * @return: 1: Slot already exists in cache (not added) + * @return: 0: Success + * @return: -1: No more free slots (caller must allocate a new member-cache table) */ +PRIVATE NONNULL((1, 2)) int DCALL Dee_membercache_table_addslot(struct Dee_membercache_table *__restrict self, struct Dee_membercache_slot const *__restrict item, bool allow_bad_hash_ratios) { @@ -235,7 +238,7 @@ Dee_membercache_table_addslot(struct Dee_membercache_table *__restrict self, size_t size = atomic_read(&self->mc_size); if (allow_bad_hash_ratios ? (size * 1) >= self->mc_mask : (size * 2) >= self->mc_mask) - return false; /* You should (or need to) allocate a new table. */ + return -1; /* You should (or need to) allocate a new table. */ if (atomic_cmpxch_or_write(&self->mc_size, size, size + 1)) break; } @@ -269,7 +272,7 @@ Dee_membercache_table_addslot(struct Dee_membercache_table *__restrict self, * our caller that their new element is already cached, even * if that *may* not actually be the case. */ atomic_dec(&self->mc_size); - return true; + return 2; } #endif /* !CONFIG_NO_THREADS */ @@ -282,11 +285,11 @@ Dee_membercache_table_addslot(struct Dee_membercache_table *__restrict self, /* Already in cache! * -> Free the slot we allocated above and indicate success to our caller. */ atomic_dec(&self->mc_size); - return true; + return 1; } /* Not found. - Try to allocate this empty slot. - * If it's no longer empty, */ + * If it's no longer empty, start over */ if (!atomic_cmpxch_or_write(&slot->mcs_type, MEMBERCACHE_UNUSED, MEMBERCACHE_UNINITIALIZED)) @@ -305,11 +308,54 @@ Dee_membercache_table_addslot(struct Dee_membercache_table *__restrict self, COMPILER_OFFSETAFTER(struct Dee_membercache_slot, mcs_type)); COMPILER_WRITE_BARRIER(); /* The type-field must be written last! */ atomic_write(&slot->mcs_type, item->mcs_type); - return true; + return 0; } -PRIVATE NONNULL((1, 2)) bool DCALL +#ifndef Dee_DPRINT_IS_NOOP +PRIVATE char const membercache_type_names[][16] = { + /* [MEMBERCACHE_UNUSED ] = */ "??UNUSED", + /* [MEMBERCACHE_UNINITIALIZED ] = */ "??UNINITIALIZED", + /* [MEMBERCACHE_METHOD ] = */ "method", + /* [MEMBERCACHE_GETSET ] = */ "getset", + /* [MEMBERCACHE_MEMBER ] = */ "member", + /* [MEMBERCACHE_ATTRIB ] = */ "attrib", + /* [MEMBERCACHE_INSTANCE_METHOD] = */ "instance_method", + /* [MEMBERCACHE_INSTANCE_GETSET] = */ "instance_getset", + /* [MEMBERCACHE_INSTANCE_MEMBER] = */ "instance_member", + /* [MEMBERCACHE_INSTANCE_ATTRIB] = */ "instance_attrib", +}; + +#define PRIVATE_IS_KNOWN_TYPETYPE(x) \ + ((x) == &DeeType_Type || (x) == &DeeFileType_Type) +#define MEMBERCACHE_GETTYPENAME(x) \ + (PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_cache)->ob_type) \ + ? COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_cache)->tp_name \ + : PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_class_cache)->ob_type) \ + ? COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_class_cache)->tp_name \ + : "?") +#define MEMBERCACHE_GETCLASSNAME(x) \ + (PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_cache)->ob_type) \ + ? "tp_cache" \ + : PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_class_cache)->ob_type) \ + ? "tp_class_cache" \ + : "?") + + +PRIVATE NONNULL((1, 2)) void DCALL +Dee_membercache_addslot_log_success(struct Dee_membercache *__restrict self, + struct Dee_membercache_slot const *__restrict slot) { + Dee_DPRINTF("[RT] Cached %s `%s.%s' in `%s' (%s)\n", + membercache_type_names[slot->mcs_type], + slot->mcs_decl->tp_name, slot->mcs_attrib.a_name, + MEMBERCACHE_GETTYPENAME(self), + MEMBERCACHE_GETCLASSNAME(self)); +} +#else /* !Dee_DPRINT_IS_NOOP */ +#define Dee_membercache_addslot_log_success(self, slot) (void)0 +#endif /* Dee_DPRINT_IS_NOOP */ + +PRIVATE NONNULL((1, 2)) int DCALL Dee_membercache_addslot(struct Dee_membercache *__restrict self, struct Dee_membercache_slot const *__restrict slot) { DREF struct Dee_membercache_table *old_table; @@ -319,14 +365,16 @@ Dee_membercache_addslot(struct Dee_membercache *__restrict self, Dee_membercache_tabuse_inc(self); old_table = atomic_read(&self->mc_table); if (old_table != NULL) { + int status; Dee_membercache_table_incref(old_table); Dee_membercache_tabuse_dec(self); /* Try to add the slot to an existing cache-table. */ do_operate_with_old_table: - if (Dee_membercache_table_addslot(old_table, slot, false)) { + status = Dee_membercache_table_addslot(old_table, slot, false); + if (status >= 0) { Dee_membercache_table_decref(old_table); - return true; + return status; } } else { Dee_membercache_tabuse_dec(self); @@ -339,11 +387,15 @@ Dee_membercache_addslot(struct Dee_membercache *__restrict self, /* Failed to create a new table -> try again to add to the * existing table, but ignore bad hash characteristics this * time around. */ - bool result = false; + int result = -1; if (old_table != NULL) { /* It doesn't matter if this addslot() call succeeds or not... */ - result = Dee_membercache_table_addslot(old_table, slot, true); + result = Dee_membercache_table_addslot(old_table, slot, true) >= 0; Dee_membercache_table_decref(old_table); +#ifndef Dee_DPRINT_IS_NOOP + if (result == 0) + Dee_membercache_addslot_log_success(self, slot); +#endif /* !Dee_DPRINT_IS_NOOP */ } return result; } @@ -389,29 +441,18 @@ Dee_membercache_addslot(struct Dee_membercache *__restrict self, membercache_list_lock_release(); } - return true; + Dee_membercache_addslot_log_success(self, slot); + return 0; } -#define PRIVATE_IS_KNOWN_TYPETYPE(x) \ - ((x) == &DeeType_Type || (x) == &DeeFileType_Type) -#define MEMBERCACHE_GETTYPENAME(x) \ - (PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_cache)->ob_type) \ - ? COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_cache)->tp_name \ - : PRIVATE_IS_KNOWN_TYPETYPE(COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_class_cache)->ob_type) \ - ? COMPILER_CONTAINER_OF(x, DeeTypeObject, tp_class_cache)->tp_name \ - : "?") - -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmethod(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_method const *method) { struct Dee_membercache_slot slot; - Dee_DPRINTF("[RT] Caching method `%s.%s' in `%s'\n", - decl->tp_name, method->m_name, - MEMBERCACHE_GETTYPENAME(self)); slot.mcs_type = MEMBERCACHE_METHOD; slot.mcs_hash = hash; slot.mcs_decl = decl; @@ -419,15 +460,12 @@ Dee_membercache_addmethod(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_method const *method) { struct Dee_membercache_slot slot; ASSERT(self != &decl->tp_cache); - Dee_DPRINTF("[RT] Caching instance_method `%s.%s' in `%s'\n", - decl->tp_name, method->m_name, - MEMBERCACHE_GETTYPENAME(self)); slot.mcs_type = MEMBERCACHE_INSTANCE_METHOD; slot.mcs_hash = hash; slot.mcs_decl = decl; @@ -435,14 +473,11 @@ Dee_membercache_addinstancemethod(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addgetset(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_getset const *getset) { struct Dee_membercache_slot slot; - Dee_DPRINTF("[RT] Caching getset `%s.%s' in `%s'\n", - decl->tp_name, getset->gs_name, - MEMBERCACHE_GETTYPENAME(self)); slot.mcs_type = MEMBERCACHE_GETSET; slot.mcs_hash = hash; slot.mcs_decl = decl; @@ -450,14 +485,11 @@ Dee_membercache_addgetset(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_getset const *getset) { struct Dee_membercache_slot slot; - Dee_DPRINTF("[RT] Caching instance_getset `%s.%s' in `%s'\n", - decl->tp_name, getset->gs_name, - MEMBERCACHE_GETTYPENAME(self)); ASSERT(self != &decl->tp_cache); slot.mcs_type = MEMBERCACHE_INSTANCE_GETSET; slot.mcs_hash = hash; @@ -466,14 +498,11 @@ Dee_membercache_addinstancegetset(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmember(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_member const *member) { struct Dee_membercache_slot slot; - Dee_DPRINTF("[RT] Caching member `%s.%s' in `%s'\n", - decl->tp_name, member->m_name, - MEMBERCACHE_GETTYPENAME(self)); slot.mcs_type = MEMBERCACHE_MEMBER; slot.mcs_hash = hash; slot.mcs_decl = decl; @@ -481,14 +510,11 @@ Dee_membercache_addmember(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemember(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct type_member const *member) { struct Dee_membercache_slot slot; - Dee_DPRINTF("[RT] Caching instance_member `%s.%s' in `%s'\n", - decl->tp_name, member->m_name, - MEMBERCACHE_GETTYPENAME(self)); ASSERT(self != &decl->tp_cache); slot.mcs_type = MEMBERCACHE_INSTANCE_MEMBER; slot.mcs_hash = hash; @@ -497,38 +523,30 @@ Dee_membercache_addinstancemember(struct Dee_membercache *self, return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addattrib(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct class_attribute *attrib) { struct Dee_membercache_slot slot; - char const *name = DeeString_STR(attrib->ca_name); - Dee_DPRINTF("[RT] Caching attribute `%s.%s' in `%s'\n", - decl->tp_name, name, - MEMBERCACHE_GETTYPENAME(self)); slot.mcs_type = MEMBERCACHE_ATTRIB; slot.mcs_hash = hash; slot.mcs_decl = decl; - slot.mcs_attrib.a_name = name; + slot.mcs_attrib.a_name = DeeString_STR(attrib->ca_name); slot.mcs_attrib.a_attr = attrib; slot.mcs_attrib.a_desc = DeeClass_DESC(decl); return Dee_membercache_addslot(self, &slot); } -INTERN NONNULL((1, 2, 4)) bool DCALL +INTERN NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstanceattrib(struct Dee_membercache *self, DeeTypeObject *decl, dhash_t hash, struct class_attribute *attrib) { struct Dee_membercache_slot slot; - char const *name = DeeString_STR(attrib->ca_name); - Dee_DPRINTF("[RT] Caching instance_attribute `%s.%s' in `%s'\n", - decl->tp_name, name, - MEMBERCACHE_GETTYPENAME(self)); ASSERT(self != &decl->tp_cache); slot.mcs_type = MEMBERCACHE_INSTANCE_ATTRIB; slot.mcs_hash = hash; slot.mcs_decl = decl; - slot.mcs_attrib.a_name = name; + slot.mcs_attrib.a_name = DeeString_STR(attrib->ca_name); slot.mcs_attrib.a_attr = attrib; slot.mcs_attrib.a_desc = DeeClass_DESC(decl); return Dee_membercache_addslot(self, &slot); @@ -547,6 +565,247 @@ Dee_membercache_addinstanceattrib(struct Dee_membercache *self, #define Dee_membercache_releasetable(self, table) \ Dee_membercache_table_decref(table) +/* Patch a member cache slot + * @return: 1: Slot cannot be patched like that + * @return: 0: Success + * @return: -1: No cache table allocated */ +PRIVATE NONNULL((1, 2, 3, 6, 7)) int DCALL +Dee_membercache_patch(struct Dee_membercache *self, DeeTypeObject *decl, + char const *attr, Dee_hash_t hash, uintptr_t attr_type, + bool (DCALL *do_patch)(struct Dee_membercache_slot *slot, + void const *new_data, + void const *old_data), + void const *new_data, void const *old_data) { + int result = 1; + Dee_hash_t i, perturb; + DREF struct Dee_membercache_table *table; + if unlikely(!Dee_membercache_acquiretable(self, &table)) + return -1; + perturb = i = Dee_membercache_table_hashst(table, hash); + for (;; Dee_membercache_table_hashnx(i, perturb)) { + struct Dee_membercache_slot *item; + uint16_t type; + item = Dee_membercache_table_hashit(table, i); + type = atomic_read(&item->mcs_type); + if (type == MEMBERCACHE_UNUSED) + break; + if (item->mcs_hash != hash) + continue; + if unlikely(type == MEMBERCACHE_UNINITIALIZED) + continue; /* Don't dereference uninitialized items! */ + if (strcmp(item->mcs_name, attr) != 0) + continue; + + /* Ensure that the attribute type matches. */ + if (type != attr_type) + return 1; + + /* Found it! -> now patch it */ + if ((*do_patch)(item, new_data, old_data)) { + atomic_write(&item->mcs_decl, decl); + result = 0; + Dee_DPRINTF("[RT] Patched %s `%s.%s' in `%s' (%s)\n", + membercache_type_names[attr_type], + decl->tp_name, attr, + MEMBERCACHE_GETTYPENAME(self), + MEMBERCACHE_GETCLASSNAME(self)); + } + break; + } + Dee_membercache_releasetable(self, table); + return result; +} + +PRIVATE WUNUSED NONNULL((1, 2)) bool DCALL +do_patch_method(struct Dee_membercache_slot *slot, void const *new_data, void const *old_data) { + bool result = false; + struct type_method const *new_method = (struct type_method const *)new_data; + struct type_method const *old_method = (struct type_method const *)old_data; + if ((slot->mcs_method.m_flag & TYPE_METHOD_FKWDS) != + (new_method->m_flag & TYPE_METHOD_FKWDS)) + return false; + if (old_method) { + result = atomic_cmpxch(&slot->mcs_method.m_func, + old_method->m_func, + new_method->m_func); + } else { + atomic_write(&slot->mcs_method.m_func, new_method->m_func); + result = true; + } + return result; +} + + +PRIVATE NONNULL((1, 2, 4)) int DCALL +Dee_membercache_patchmethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_method const *new_method, + /*0..1*/ struct type_method const *old_method) { +#ifdef CONFIG_NO_THREADS + int result = Dee_membercache_addmethod(self, decl, hash, new_method); +#else /* CONFIG_NO_THREADS */ + int result; + while ((result = Dee_membercache_addmethod(self, decl, hash, new_method)) == 2) + SCHED_YIELD(); +#endif /* !CONFIG_NO_THREADS */ + if (result > 0) { + /* Entry already exists (try to patch it) */ + result = Dee_membercache_patch(self, decl, new_method->m_name, hash, + MEMBERCACHE_METHOD, &do_patch_method, + new_method, old_method); + } + return result; +} + +PRIVATE NONNULL((1, 2, 4)) int DCALL +Dee_membercache_patchinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_method const *new_method, + /*0..1*/ struct type_method const *old_method) { +#ifdef CONFIG_NO_THREADS + int result = Dee_membercache_addinstancemethod(self, decl, hash, new_method); +#else /* CONFIG_NO_THREADS */ + int result; + while ((result = Dee_membercache_addinstancemethod(self, decl, hash, new_method)) == 2) + SCHED_YIELD(); +#endif /* !CONFIG_NO_THREADS */ + if (result > 0) { + /* Entry already exists (try to patch it) */ + result = Dee_membercache_patch(self, decl, new_method->m_name, hash, + MEMBERCACHE_INSTANCE_METHOD, &do_patch_method, + new_method, old_method); + } + return result; +} + + +PRIVATE WUNUSED NONNULL((1, 2)) bool DCALL +do_patch_getset(struct Dee_membercache_slot *slot, void const *new_data, void const *old_data) { + bool result = false; + struct type_getset const *new_getset = (struct type_getset const *)new_data; + struct type_getset const *old_getset = (struct type_getset const *)old_data; + if (old_getset) { + if (atomic_cmpxch(&slot->mcs_getset.gs_get, old_getset->gs_get, new_getset->gs_get)) + result = true; + if (atomic_cmpxch(&slot->mcs_getset.gs_del, old_getset->gs_del, new_getset->gs_del)) + result = true; + if (atomic_cmpxch(&slot->mcs_getset.gs_set, old_getset->gs_set, new_getset->gs_set)) + result = true; + if (atomic_cmpxch(&slot->mcs_getset.gs_bound, old_getset->gs_bound, new_getset->gs_bound)) + result = true; + } else { + atomic_write(&slot->mcs_getset.gs_get, new_getset->gs_get); + atomic_write(&slot->mcs_getset.gs_del, new_getset->gs_del); + atomic_write(&slot->mcs_getset.gs_set, new_getset->gs_set); + atomic_write(&slot->mcs_getset.gs_bound, new_getset->gs_bound); + result = true; + } + return result; +} + + +PRIVATE NONNULL((1, 2, 4)) int DCALL +Dee_membercache_patchgetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_getset const *new_getset, + /*0..1*/ struct type_getset const *old_getset) { +#ifdef CONFIG_NO_THREADS + int result = Dee_membercache_addgetset(self, decl, hash, new_getset); +#else /* CONFIG_NO_THREADS */ + int result; + while ((result = Dee_membercache_addgetset(self, decl, hash, new_getset)) == 2) + SCHED_YIELD(); +#endif /* !CONFIG_NO_THREADS */ + if (result > 0) { + /* Entry already exists (try to patch it) */ + result = Dee_membercache_patch(self, decl, new_getset->gs_name, hash, + MEMBERCACHE_GETSET, &do_patch_getset, + new_getset, old_getset); + } + return result; +} + +PRIVATE NONNULL((1, 2, 4)) int DCALL +Dee_membercache_patchinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_getset const *new_getset, + /*0..1*/ struct type_getset const *old_getset) { +#ifdef CONFIG_NO_THREADS + int result = Dee_membercache_addinstancegetset(self, decl, hash, new_getset); +#else /* CONFIG_NO_THREADS */ + int result; + while ((result = Dee_membercache_addinstancegetset(self, decl, hash, new_getset)) == 2) + SCHED_YIELD(); +#endif /* !CONFIG_NO_THREADS */ + if (result > 0) { + /* Entry already exists (try to patch it) */ + result = Dee_membercache_patch(self, decl, new_getset->gs_name, hash, + MEMBERCACHE_INSTANCE_GETSET, &do_patch_getset, + new_getset, old_getset); + } + return result; +} + + + +/* Try to add the specified attribute to the cache of "self". + * - If this fails due to OOM, return `-1', but DON'T throw an exception + * If the MRO cache already contains an entry for the named attribute: + * - Verify that the existing entry is for the same type of attribute (`MEMBERCACHE_*'), + * such that it can be patched without having to alter `mcs_type' (since having to do + * so would result in a non-resolvable race condition where another thread is currently + * dereferencing the function pointers from the existing entry). + * If this verification fails, return `1'. + * - For `DeeTypeMRO_Patch*Method', it is also verified that both the + * old and new function pointers share a common `TYPE_METHOD_FKWDS'. + * - If the type matches, the pre-existing cache entries pointers are patched such that + * they will now reference those from the given parameters. + * Note that for this purpose, this exchange is atomic for each individual function + * pointer (but not all pointers as a whole) -- in the case of `DeeTypeMRO_Patch*GetSet', + * another thread may invoke (e.g.) an out-dated `gs_del' after `gs_get' was already + * patched. + * + * NOTE: Generally, only use these functions for self-optimizing methods in base-classes + * that wish to skip certain type-dependent verification steps during future calls. + * (e.g. `Sequence.first', `Mapping.keys') + * + * @param: old_*: [0..1] When non-NULL, use these values for compare-exchange operations. + * But also note that when there are many function pointers, some may + * be set, while others cannot be -- here, an attempt to exchange + * pointers is made for *all* pointers, and success is indicated if + * at least 1 pointer could be exchanged. + * @return: 1: Failure (cache entry cannot be patched like this) + * @return: 0: Success + * @return: -1: Patching failed due to OOM (but no error was thrown!) */ + +PUBLIC NONNULL((1, 2, 4)) int DCALL +DeeTypeMRO_PatchMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_method const *new_method, /*0..1*/ struct type_method const *old_method) { + int result = Dee_membercache_patchmethod(&self->tp_cache, decl, hash, new_method, old_method); + if likely(result == 0) + result = Dee_membercache_patchinstancemethod(&self->tp_class_cache, decl, hash, new_method, old_method); + return result; +} + +PUBLIC NONNULL((1, 2, 4)) int DCALL +DeeTypeMRO_PatchGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset) { + int result = Dee_membercache_patchgetset(&self->tp_cache, decl, hash, new_getset, old_getset); + if likely(result == 0) + result = Dee_membercache_patchinstancegetset(&self->tp_class_cache, decl, hash, new_getset, old_getset); + return result; +} + +PUBLIC NONNULL((1, 2, 4)) int DCALL +DeeTypeMRO_PatchClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_method const *new_method, /*0..1*/ struct type_method const *old_method) { + return Dee_membercache_patchmethod(&self->tp_class_cache, decl, hash, new_method, old_method); +} + +PUBLIC NONNULL((1, 2, 4)) int DCALL +DeeTypeMRO_PatchClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, + struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset) { + return Dee_membercache_patchgetset(&self->tp_class_cache, decl, hash, new_getset, old_getset); +} + + + #ifndef CONFIG_CALLTUPLE_OPTIMIZATIONS /* TODO: For binary compat: * #define DEFINE_DeeType_CallCachedAttrStringHashTuple