@@ -3420,6 +3420,130 @@ test_pyobject_setallocators(PyObject *self)
34203420 return test_setallocators (PYMEM_DOMAIN_OBJ );
34213421}
34223422
3423+ /* Most part of the following code is inherited from the pyfailmalloc project
3424+ * written by Victor Stinner. */
3425+ static struct {
3426+ int installed ;
3427+ PyMemAllocatorEx raw ;
3428+ PyMemAllocatorEx mem ;
3429+ PyMemAllocatorEx obj ;
3430+ } FmHook ;
3431+
3432+ static struct {
3433+ int start ;
3434+ int stop ;
3435+ Py_ssize_t count ;
3436+ } FmData ;
3437+
3438+ static int
3439+ fm_nomemory (void )
3440+ {
3441+ FmData .count ++ ;
3442+ if (FmData .count > FmData .start &&
3443+ (FmData .stop <= 0 || FmData .count <= FmData .stop )) {
3444+ return 1 ;
3445+ }
3446+ return 0 ;
3447+ }
3448+
3449+ static void *
3450+ hook_fmalloc (void * ctx , size_t size )
3451+ {
3452+ PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
3453+ if (fm_nomemory ()) {
3454+ return NULL ;
3455+ }
3456+ return alloc -> malloc (alloc -> ctx , size );
3457+ }
3458+
3459+ static void *
3460+ hook_fcalloc (void * ctx , size_t nelem , size_t elsize )
3461+ {
3462+ PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
3463+ if (fm_nomemory ()) {
3464+ return NULL ;
3465+ }
3466+ return alloc -> calloc (alloc -> ctx , nelem , elsize );
3467+ }
3468+
3469+ static void *
3470+ hook_frealloc (void * ctx , void * ptr , size_t new_size )
3471+ {
3472+ PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
3473+ if (fm_nomemory ()) {
3474+ return NULL ;
3475+ }
3476+ return alloc -> realloc (alloc -> ctx , ptr , new_size );
3477+ }
3478+
3479+ static void
3480+ hook_ffree (void * ctx , void * ptr )
3481+ {
3482+ PyMemAllocatorEx * alloc = (PyMemAllocatorEx * )ctx ;
3483+ alloc -> free (alloc -> ctx , ptr );
3484+ }
3485+
3486+ static void
3487+ fm_setup_hooks (void )
3488+ {
3489+ PyMemAllocatorEx alloc ;
3490+
3491+ if (FmHook .installed ) {
3492+ return ;
3493+ }
3494+ FmHook .installed = 1 ;
3495+
3496+ alloc .malloc = hook_fmalloc ;
3497+ alloc .calloc = hook_fcalloc ;
3498+ alloc .realloc = hook_frealloc ;
3499+ alloc .free = hook_ffree ;
3500+ PyMem_GetAllocator (PYMEM_DOMAIN_RAW , & FmHook .raw );
3501+ PyMem_GetAllocator (PYMEM_DOMAIN_MEM , & FmHook .mem );
3502+ PyMem_GetAllocator (PYMEM_DOMAIN_OBJ , & FmHook .obj );
3503+
3504+ alloc .ctx = & FmHook .raw ;
3505+ PyMem_SetAllocator (PYMEM_DOMAIN_RAW , & alloc );
3506+
3507+ alloc .ctx = & FmHook .mem ;
3508+ PyMem_SetAllocator (PYMEM_DOMAIN_MEM , & alloc );
3509+
3510+ alloc .ctx = & FmHook .obj ;
3511+ PyMem_SetAllocator (PYMEM_DOMAIN_OBJ , & alloc );
3512+ }
3513+
3514+ static void
3515+ fm_remove_hooks (void )
3516+ {
3517+ if (FmHook .installed ) {
3518+ FmHook .installed = 0 ;
3519+ PyMem_SetAllocator (PYMEM_DOMAIN_RAW , & FmHook .raw );
3520+ PyMem_SetAllocator (PYMEM_DOMAIN_MEM , & FmHook .mem );
3521+ PyMem_SetAllocator (PYMEM_DOMAIN_OBJ , & FmHook .obj );
3522+ }
3523+ }
3524+
3525+ static PyObject *
3526+ set_nomemory (PyObject * self , PyObject * args )
3527+ {
3528+ /* Memory allocation fails after 'start' allocation requests, and until
3529+ * 'stop' allocation requests except when 'stop' is negative or equal
3530+ * to 0 (default) in which case allocation failures never stop. */
3531+ FmData .count = 0 ;
3532+ FmData .stop = 0 ;
3533+ if (!PyArg_ParseTuple (args , "i|i" , & FmData .start , & FmData .stop )) {
3534+ return NULL ;
3535+ }
3536+ fm_setup_hooks ();
3537+ Py_RETURN_NONE ;
3538+ }
3539+
3540+ static PyObject *
3541+ remove_mem_hooks (PyObject * self )
3542+ {
3543+ fm_remove_hooks ();
3544+ Py_RETURN_NONE ;
3545+ }
3546+
34233547PyDoc_STRVAR (docstring_empty ,
34243548""
34253549);
@@ -4287,6 +4411,10 @@ static PyMethodDef TestMethods[] = {
42874411 (PyCFunction )test_pymem_setallocators , METH_NOARGS },
42884412 {"test_pyobject_setallocators" ,
42894413 (PyCFunction )test_pyobject_setallocators , METH_NOARGS },
4414+ {"set_nomemory" , (PyCFunction )set_nomemory , METH_VARARGS ,
4415+ PyDoc_STR ("set_nomemory(start:int, stop:int = 0)" )},
4416+ {"remove_mem_hooks" , (PyCFunction )remove_mem_hooks , METH_NOARGS ,
4417+ PyDoc_STR ("Remove memory hooks." )},
42904418 {"no_docstring" ,
42914419 (PyCFunction )test_with_docstring , METH_NOARGS },
42924420 {"docstring_empty" ,
0 commit comments