mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	GH-100000: Cleanup and polish various watchers code (GH-99998)
* Initialize `type_watchers` array to `NULL`s * Optimize code watchers notification * Optimize func watchers notification
This commit is contained in:
		
							parent
							
								
									aa8591e9ca
								
							
						
					
					
						commit
						ae83c78215
					
				
					 6 changed files with 37 additions and 14 deletions
				
			
		|  | @ -142,7 +142,6 @@ struct _is { | ||||||
|     // Initialized to _PyEval_EvalFrameDefault().
 |     // Initialized to _PyEval_EvalFrameDefault().
 | ||||||
|     _PyFrameEvalFunction eval_frame; |     _PyFrameEvalFunction eval_frame; | ||||||
| 
 | 
 | ||||||
|     PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS]; |  | ||||||
|     PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS]; |     PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS]; | ||||||
|     // One bit is set for each non-NULL entry in func_watchers
 |     // One bit is set for each non-NULL entry in func_watchers
 | ||||||
|     uint8_t active_func_watchers; |     uint8_t active_func_watchers; | ||||||
|  |  | ||||||
|  | @ -630,14 +630,16 @@ static PyMethodDef test_methods[] = { | ||||||
|     {"clear_dict_watcher",       clear_dict_watcher,      METH_O,       NULL}, |     {"clear_dict_watcher",       clear_dict_watcher,      METH_O,       NULL}, | ||||||
|     {"watch_dict",               watch_dict,              METH_VARARGS, NULL}, |     {"watch_dict",               watch_dict,              METH_VARARGS, NULL}, | ||||||
|     {"unwatch_dict",             unwatch_dict,            METH_VARARGS, NULL}, |     {"unwatch_dict",             unwatch_dict,            METH_VARARGS, NULL}, | ||||||
|     {"get_dict_watcher_events",  get_dict_watcher_events, METH_NOARGS,  NULL}, |     {"get_dict_watcher_events", | ||||||
|  |      (PyCFunction) get_dict_watcher_events,               METH_NOARGS,  NULL}, | ||||||
| 
 | 
 | ||||||
|     // Type watchers.
 |     // Type watchers.
 | ||||||
|     {"add_type_watcher",         add_type_watcher,        METH_O,       NULL}, |     {"add_type_watcher",         add_type_watcher,        METH_O,       NULL}, | ||||||
|     {"clear_type_watcher",       clear_type_watcher,      METH_O,       NULL}, |     {"clear_type_watcher",       clear_type_watcher,      METH_O,       NULL}, | ||||||
|     {"watch_type",               watch_type,              METH_VARARGS, NULL}, |     {"watch_type",               watch_type,              METH_VARARGS, NULL}, | ||||||
|     {"unwatch_type",             unwatch_type,            METH_VARARGS, NULL}, |     {"unwatch_type",             unwatch_type,            METH_VARARGS, NULL}, | ||||||
|     {"get_type_modified_events", get_type_modified_events, METH_NOARGS, NULL}, |     {"get_type_modified_events", | ||||||
|  |      (PyCFunction) get_type_modified_events,              METH_NOARGS, NULL}, | ||||||
| 
 | 
 | ||||||
|     // Code object watchers.
 |     // Code object watchers.
 | ||||||
|     {"add_code_watcher",         add_code_watcher,        METH_O,       NULL}, |     {"add_code_watcher",         add_code_watcher,        METH_O,       NULL}, | ||||||
|  |  | ||||||
|  | @ -15,14 +15,21 @@ static void | ||||||
| notify_code_watchers(PyCodeEvent event, PyCodeObject *co) | notify_code_watchers(PyCodeEvent event, PyCodeObject *co) | ||||||
| { | { | ||||||
|     PyInterpreterState *interp = _PyInterpreterState_GET(); |     PyInterpreterState *interp = _PyInterpreterState_GET(); | ||||||
|     if (interp->active_code_watchers) { |     assert(interp->_initialized); | ||||||
|         assert(interp->_initialized); |     uint8_t bits = interp->active_code_watchers; | ||||||
|         for (int i = 0; i < CODE_MAX_WATCHERS; i++) { |     int i = 0; | ||||||
|  |     while (bits) { | ||||||
|  |         assert(i < CODE_MAX_WATCHERS); | ||||||
|  |         if (bits & 1) { | ||||||
|             PyCode_WatchCallback cb = interp->code_watchers[i]; |             PyCode_WatchCallback cb = interp->code_watchers[i]; | ||||||
|             if ((cb != NULL) && (cb(event, co) < 0)) { |             // callback must be non-null if the watcher bit is set
 | ||||||
|  |             assert(cb != NULL); | ||||||
|  |             if (cb(event, co) < 0) { | ||||||
|                 PyErr_WriteUnraisable((PyObject *) co); |                 PyErr_WriteUnraisable((PyObject *) co); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         i++; | ||||||
|  |         bits >>= 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,11 +12,20 @@ static void | ||||||
| notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event, | notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event, | ||||||
|                      PyFunctionObject *func, PyObject *new_value) |                      PyFunctionObject *func, PyObject *new_value) | ||||||
| { | { | ||||||
|     for (int i = 0; i < FUNC_MAX_WATCHERS; i++) { |     uint8_t bits = interp->active_func_watchers; | ||||||
|         PyFunction_WatchCallback cb = interp->func_watchers[i]; |     int i = 0; | ||||||
|         if ((cb != NULL) && (cb(event, func, new_value) < 0)) { |     while (bits) { | ||||||
|             PyErr_WriteUnraisable((PyObject *) func); |         assert(i < FUNC_MAX_WATCHERS); | ||||||
|  |         if (bits & 1) { | ||||||
|  |             PyFunction_WatchCallback cb = interp->func_watchers[i]; | ||||||
|  |             // callback must be non-null if the watcher bit is set
 | ||||||
|  |             assert(cb != NULL); | ||||||
|  |             if (cb(event, func, new_value) < 0) { | ||||||
|  |                 PyErr_WriteUnraisable((PyObject *) func); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |         i++; | ||||||
|  |         bits >>= 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -25,6 +34,7 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, | ||||||
|                   PyObject *new_value) |                   PyObject *new_value) | ||||||
| { | { | ||||||
|     PyInterpreterState *interp = _PyInterpreterState_GET(); |     PyInterpreterState *interp = _PyInterpreterState_GET(); | ||||||
|  |     assert(interp->_initialized); | ||||||
|     if (interp->active_func_watchers) { |     if (interp->active_func_watchers) { | ||||||
|         notify_func_watchers(interp, event, func, new_value); |         notify_func_watchers(interp, event, func, new_value); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -485,23 +485,24 @@ PyType_Modified(PyTypeObject *type) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Notify registered type watchers, if any
 | ||||||
|     if (type->tp_watched) { |     if (type->tp_watched) { | ||||||
|         PyInterpreterState *interp = _PyInterpreterState_GET(); |         PyInterpreterState *interp = _PyInterpreterState_GET(); | ||||||
|         int bits = type->tp_watched; |         int bits = type->tp_watched; | ||||||
|         int i = 0; |         int i = 0; | ||||||
|         while(bits && i < TYPE_MAX_WATCHERS) { |         while (bits) { | ||||||
|  |             assert(i < TYPE_MAX_WATCHERS); | ||||||
|             if (bits & 1) { |             if (bits & 1) { | ||||||
|                 PyType_WatchCallback cb = interp->type_watchers[i]; |                 PyType_WatchCallback cb = interp->type_watchers[i]; | ||||||
|                 if (cb && (cb(type) < 0)) { |                 if (cb && (cb(type) < 0)) { | ||||||
|                     PyErr_WriteUnraisable((PyObject *)type); |                     PyErr_WriteUnraisable((PyObject *)type); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             i += 1; |             i++; | ||||||
|             bits >>= 1; |             bits >>= 1; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; |     type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; | ||||||
|     type->tp_version_tag = 0; /* 0 is not a valid version tag */ |     type->tp_version_tag = 0; /* 0 is not a valid version tag */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -461,6 +461,10 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) | ||||||
|         interp->dict_state.watchers[i] = NULL; |         interp->dict_state.watchers[i] = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     for (int i=0; i < TYPE_MAX_WATCHERS; i++) { | ||||||
|  |         interp->type_watchers[i] = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     for (int i=0; i < FUNC_MAX_WATCHERS; i++) { |     for (int i=0; i < FUNC_MAX_WATCHERS; i++) { | ||||||
|         interp->func_watchers[i] = NULL; |         interp->func_watchers[i] = NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Itamar Ostricher
						Itamar Ostricher