diff options
author | Carl Meyer <carl@oddbird.net> | 2022-10-07 00:08:00 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-07 00:08:00 (GMT) |
commit | a4b7794887929f82c532fcd055326954ff1197ce (patch) | |
tree | 257e2dc783858251f893d75c17663913b05a0fad /Include | |
parent | 683ab859554c34831fcecc854de35745d7fd603c (diff) | |
download | cpython-a4b7794887929f82c532fcd055326954ff1197ce.zip cpython-a4b7794887929f82c532fcd055326954ff1197ce.tar.gz cpython-a4b7794887929f82c532fcd055326954ff1197ce.tar.bz2 |
GH-91052: Add C API for watching dictionaries (GH-31787)
Diffstat (limited to 'Include')
-rw-r--r-- | Include/cpython/dictobject.h | 23 | ||||
-rw-r--r-- | Include/internal/pycore_dict.h | 27 | ||||
-rw-r--r-- | Include/internal/pycore_interp.h | 2 |
3 files changed, 51 insertions, 1 deletions
diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 565ad791..f8a74a5 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -83,3 +83,26 @@ typedef struct { PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *); PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); + +/* Dictionary watchers */ + +typedef enum { + PyDict_EVENT_ADDED, + PyDict_EVENT_MODIFIED, + PyDict_EVENT_DELETED, + PyDict_EVENT_CLONED, + PyDict_EVENT_CLEARED, + PyDict_EVENT_DEALLOCATED, +} PyDict_WatchEvent; + +// Callback to be invoked when a watched dict is cleared, dealloced, or modified. +// In clear/dealloc case, key and new_value will be NULL. Otherwise, new_value will be the +// new value for key, NULL if key is being deleted. +typedef int(*PyDict_WatchCallback)(PyDict_WatchEvent event, PyObject* dict, PyObject* key, PyObject* new_value); + +// Register/unregister a dict-watcher callback +PyAPI_FUNC(int) PyDict_AddWatcher(PyDict_WatchCallback callback); +PyAPI_FUNC(int) PyDict_ClearWatcher(int watcher_id); + +// Mark given dictionary as "watched" (callback will be called if it is modified) +PyAPI_FUNC(int) PyDict_Watch(int watcher_id, PyObject* dict); diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 4640929..ae4094a 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -154,7 +154,32 @@ struct _dictvalues { extern uint64_t _pydict_global_version; -#define DICT_NEXT_VERSION() (++_pydict_global_version) +#define DICT_MAX_WATCHERS 8 +#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS) +#define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1) + +#define DICT_NEXT_VERSION() (_pydict_global_version += DICT_VERSION_INCREMENT) + +void +_PyDict_SendEvent(int watcher_bits, + PyDict_WatchEvent event, + PyDictObject *mp, + PyObject *key, + PyObject *value); + +static inline uint64_t +_PyDict_NotifyEvent(PyDict_WatchEvent event, + PyDictObject *mp, + PyObject *key, + PyObject *value) +{ + int watcher_bits = mp->ma_version_tag & DICT_VERSION_MASK; + if (watcher_bits) { + _PyDict_SendEvent(watcher_bits, event, mp, key, value); + return DICT_NEXT_VERSION() | watcher_bits; + } + return DICT_NEXT_VERSION(); +} extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values); extern PyObject *_PyDict_FromItems( diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b21708a..8cecd5a 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -144,6 +144,8 @@ struct _is { // Initialized to _PyEval_EvalFrameDefault(). _PyFrameEvalFunction eval_frame; + PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS]; + Py_ssize_t co_extra_user_count; freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; |