Ticket #1930: libevent-gc.patch
| File libevent-gc.patch, 17.0 kB (added by antoine, 8 months ago) |
|---|
-
twisted/python/_libevent.c
a b 54 54 typedef struct EventBaseObject { 55 55 PyObject_HEAD 56 56 struct event_base *ev_base; 57 /* A list of objects to be expired. We keep them here until the loop 58 returns, otherwise it may crash libevent (who knows). */ 59 PyObject *expiredEvents; 60 /* A dict of EventObject => None */ 61 PyObject *registeredEvents; 57 62 } EventBaseObject; 58 63 59 64 /* Forward declaration of CPython type object */ … … 67 72 struct event ev; 68 73 EventBaseObject *eventBase; 69 74 PyObject *callback; 75 /* Duplicate original event flags since they seem to be modified when we 76 arrive in the callback thunk */ 77 short flags; 70 78 } EventObject; 71 79 72 80 /* Forward declaration of CPython type object */ … … 99 107 return NULL; 100 108 } 101 109 } 110 self->expiredEvents = PyList_New(0); 111 self->registeredEvents = PyDict_New(); 102 112 return (PyObject *)self; 103 113 } 104 114 … … 127 137 return 0; 128 138 } 129 139 140 /* Internal helper, destroy expired events */ 141 static void EventBase_ExpireEvents(EventBaseObject *self) { 142 /* Destroy and recreate an empty list, so as to to decref all events 143 as well as their contents */ 144 Py_CLEAR(self->expiredEvents); 145 self->expiredEvents = PyList_New(0); 146 } 147 148 /* Internal helper, register an event */ 149 static void EventBase_RegisterEvent(EventBaseObject *self, PyObject *obj) { 150 PyDict_SetItem(self->registeredEvents, obj, Py_None); 151 } 152 153 /* Internal helper, unregister an event */ 154 static void EventBase_UnregisterEvent(EventBaseObject *self, PyObject *obj) { 155 PyDict_DelItem(self->registeredEvents, obj); 156 } 157 158 159 160 static int 161 EventBase_Traverse(EventBaseObject *self, visitproc visit, void *arg) 162 { 163 int vret; 164 165 if (self->registeredEvents) { 166 vret = visit(self->registeredEvents, arg); 167 if (vret != 0) 168 return vret; 169 } 170 if (self->expiredEvents) { 171 vret = visit(self->expiredEvents, arg); 172 if (vret != 0) 173 return vret; 174 } 175 176 return 0; 177 } 178 179 static int 180 EventBase_Clear(EventBaseObject *self) 181 { 182 Py_CLEAR(self->registeredEvents); 183 Py_CLEAR(self->expiredEvents); 184 return 0; 185 } 186 130 187 /* EventBaseObject destructor */ 131 188 static void EventBase_Dealloc(EventBaseObject *obj) { 189 EventBase_Clear(obj); 190 /* Unfortunately the following line can make libevent crash (w/ 1.4.1-beta) 191 ("event.c:241: event_base_free: Assertion `min_heap_empty(&base->timeheap)' failed.") 192 */ 193 // event_base_free(obj->ev_base); 132 194 obj->ob_type->tp_free((PyObject *)obj); 133 195 } 196 134 197 135 198 /* EventBaseObject methods */ 136 199 PyDoc_STRVAR(EventBase_LoopDoc, … … 152 215 rv = event_base_loop(self->ev_base, flags); 153 216 Py_END_ALLOW_THREADS 154 217 218 EventBase_ExpireEvents(self); 155 219 if (PyErr_Occurred()) { 156 220 return NULL; 157 221 } … … 177 241 178 242 tv.tv_sec = (long) exitAfterSecs; 179 243 tv.tv_usec = (exitAfterSecs - (long) exitAfterSecs) * 1000000; 244 Py_BEGIN_ALLOW_THREADS 180 245 rv = event_base_loopexit(self->ev_base, &tv); 246 Py_END_ALLOW_THREADS 247 248 EventBase_ExpireEvents(self); 249 if (PyErr_Occurred()) { 250 return NULL; 251 } 252 181 253 return PyInt_FromLong(rv); 182 254 } 183 255 … … 189 261 explicit call to EventBase.loopExit() or via a signal, or if a callback \n\ 190 262 raises an exception."); 191 263 static PyObject *EventBase_Dispatch(EventBaseObject *self) { 192 193 int rv = event_base_dispatch(self->ev_base); 264 int rv = 0; 265 Py_BEGIN_ALLOW_THREADS 266 rv = event_base_dispatch(self->ev_base); 267 Py_END_ALLOW_THREADS 268 EventBase_ExpireEvents(self); 194 269 if (PyErr_Occurred()) { 195 270 return NULL; 196 271 } … … 335 410 PyObject_GenericGetAttr, /*tp_getattro*/ 336 411 PyObject_GenericSetAttr, /*tp_setattro*/ 337 412 0, /*tp_as_buffer*/ 338 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE ,/*tp_flags*/413 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ 339 414 0, /*tp_doc*/ 340 0,/*tp_traverse*/341 0,/*tp_clear*/415 (traverseproc)EventBase_Traverse, /*tp_traverse*/ 416 (inquiry)EventBase_Clear, /*tp_clear*/ 342 417 0, /*tp_richcompare*/ 343 418 0, /*tp_weaklistoffset*/ 344 419 0, /*tp_iter*/ … … 353 428 0, /*tp_dictoffset*/ 354 429 (initproc)EventBase_Init, /*tp_init*/ 355 430 PyType_GenericAlloc, /*tp_alloc*/ 356 EventBase_New, /*tp_new*/ 357 PyObject_Del, /*tp_free*/ 358 0 /*tp_is_gc*/ 431 EventBase_New /*tp_new*/ 359 432 }; 360 433 361 434 /* Typechecker */ … … 376 449 static void __libevent_ev_callback(int fd, short events, void *arg) { 377 450 EventObject *ev = arg; 378 451 PyObject *result = 0; 379 PyObject *tupleArgs = PyTuple_New(3); 380 Py_INCREF((PyObject *) ev); 381 Py_INCREF((PyObject *) ev); 382 PyTuple_SET_ITEM(tupleArgs, 0, PyInt_FromLong(fd)); 383 PyTuple_SET_ITEM(tupleArgs, 1, PyInt_FromLong(events)); 384 PyTuple_SET_ITEM(tupleArgs, 2, (PyObject *) ev); 452 PyObject *tupleArgs; 385 453 PyGILState_STATE gstate; 386 454 gstate = PyGILState_Ensure(); 455 tupleArgs = Py_BuildValue("(iiO)", fd, events, ev); 456 Py_INCREF((PyObject *) ev); 387 457 result = PyObject_CallObject(ev->callback, tupleArgs); 388 Py_DECREF((PyObject *) ev);389 458 Py_CLEAR(tupleArgs); 459 if (!(ev->flags & EV_PERSIST)) { 460 /* Register the event for deletion but do not delete it right now. 461 The list will be destroyed and its contents deallocated when the 462 event loop returns. */ 463 PyList_Append(ev->eventBase->expiredEvents, (PyObject *) ev); 464 EventBase_UnregisterEvent(ev->eventBase, (PyObject *) ev); 465 } 390 466 if (result) { 391 467 Py_CLEAR(result); 392 468 } … … 397 473 /* Exit the loop, so that the error pops out to dispatch/loop. */ 398 474 event_base_loopexit(ev->ev.ev_base, &tv); 399 475 } 476 Py_DECREF((PyObject *) ev); 400 477 Safe_PyGILState_Release(gstate); 401 478 } 402 479 … … 430 507 431 508 Py_INCREF(callback); 432 509 self->callback = callback; 510 self->flags = events; 433 511 return 0; 434 512 } 513 514 static int 515 Event_Traverse(EventObject *self, visitproc visit, void *arg) 516 { 517 int vret; 518 519 if (self->eventBase) { 520 vret = visit((PyObject *) self->eventBase, arg); 521 if (vret != 0) 522 return vret; 523 } 524 if (self->callback) { 525 vret = visit(self->callback, arg); 526 if (vret != 0) 527 return vret; 528 } 529 530 return 0; 531 } 532 533 static int 534 Event_Clear(EventObject *self) 535 { 536 Py_CLEAR(self->callback); 537 Py_CLEAR(self->eventBase); 538 return 0; 539 } 540 435 541 436 542 PyDoc_STRVAR(Event_SetPriorityDoc, 437 543 "setPriority(self, priority)\n\ … … 451 557 "error setting event priority - event is either already active or priorities are not enabled"); 452 558 return NULL; 453 559 } 454 Py_INCREF(Py_None); 455 return Py_None; 560 Py_RETURN_NONE; 456 561 } 457 562 458 563 PyDoc_STRVAR(Event_AddToLoopDoc, … … 476 581 if (timeout >= 0.0) { 477 582 tv.tv_sec = (long) timeout; 478 583 tv.tv_usec = (timeout - (long) timeout) * 1000000; 479 rv = event_add(& ((EventObject *) self)->ev, &tv);584 rv = event_add(&self->ev, &tv); 480 585 } 481 586 else { 482 rv = event_add(& ((EventObject *) self)->ev, NULL);587 rv = event_add(&self->ev, NULL); 483 588 } 484 589 if (rv != 0) { 485 590 return PyErr_SetFromErrno(EventErrorObject); 486 591 } 487 Py_INCREF(self); 488 Py_INCREF(Py_None); 489 return Py_None; 592 EventBase_RegisterEvent(self->eventBase, (PyObject *) self); 593 Py_RETURN_NONE; 490 594 } 491 595 492 596 PyDoc_STRVAR(Event_RemoveFromLoopDoc, … … 498 602 if (event_del(&self->ev) < 0) { 499 603 return PyErr_SetFromErrno(EventErrorObject); 500 604 } 501 Py_DECREF(self); 502 Py_INCREF(Py_None); 503 return Py_None; 605 EventBase_UnregisterEvent(self->eventBase, (PyObject *) self); 606 Py_RETURN_NONE; 504 607 } 505 608 506 609 PyDoc_STRVAR(Event_SetEventBaseDoc, … … 510 613 static PyObject *Event_SetEventBase(EventObject *self, PyObject *args, 511 614 PyObject *kwargs) { 512 615 static char *kwlist[] = {"eventBase", NULL}; 513 PyObject *eventBase ;616 PyObject *eventBase, *old_eventBase; 514 617 int rv = 0; 515 618 516 619 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &eventBase)) { … … 527 630 return NULL; 528 631 } 529 632 Py_INCREF(eventBase); 530 Py_XDECREF(self->eventBase); 633 /* Thread-safe way of removing an attr value */ 634 old_eventBase = (PyObject *) self->eventBase; 531 635 self->eventBase = (EventBaseObject *)eventBase; 532 Py_ INCREF(Py_None);533 return Py_None;636 Py_XDECREF(old_eventBase); 637 Py_RETURN_NONE; 534 638 } 535 639 536 640 PyDoc_STRVAR(Event_PendingDoc, … … 559 663 d = tv.tv_sec + (tv.tv_usec / 1000000.0); 560 664 return PyFloat_FromDouble(d); 561 665 } 562 Py_INCREF(Py_None); 563 return Py_None; 666 Py_RETURN_NONE; 564 667 } 565 668 566 669 PyDoc_STRVAR(Event_FilenoDoc, … … 574 677 575 678 /* EventObject destructor */ 576 679 static void Event_Dealloc(EventObject *obj) { 577 Py_XDECREF(obj->eventBase); 578 Py_XDECREF(obj->callback); 680 Event_Clear(obj); 579 681 obj->ob_type->tp_free((PyObject *)obj); 580 682 } 581 683 … … 650 752 PyObject_GenericGetAttr, /*tp_getattro*/ 651 753 PyObject_GenericSetAttr, /*tp_setattro*/ 652 754 0, /*tp_as_buffer*/ 653 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /*tp_flags*/755 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ 654 756 0, /*tp_doc*/ 655 0,/*tp_traverse*/656 0,/*tp_clear*/757 (traverseproc)Event_Traverse, /*tp_traverse*/ 758 (inquiry)Event_Clear, /*tp_clear*/ 657 759 0, /*tp_richcompare*/ 658 760 0, /*tp_weaklistoffset*/ 659 761 0, /*tp_iter*/ … … 668 770 0, /*tp_dictoffset*/ 669 771 (initproc)Event_Init, /*tp_init*/ 670 772 PyType_GenericAlloc, /*tp_alloc*/ 671 Event_New, /*tp_new*/ 672 PyObject_Del, /*tp_free*/ 673 0, /*tp_is_gc*/ 773 Event_New /*tp_new*/ 674 774 }; 675 775 676 776 static PyMethodDef EventModule_Functions[] = { -
twisted/test/test_libevent.py
a b 1 # Copyright (c) 2007 -2008Twisted Matrix Laboratories.1 # Copyright (c) 2007 Twisted Matrix Laboratories. 2 2 # See LICENSE for details. 3 3 4 4 """ 5 Tests for L{libevent}wrapper.5 Tests for libevent wrapper. 6 6 """ 7 7 8 import socket, errno, sys, weakref, gc8 import socket, errno, sys, os, weakref, gc 9 9 10 10 from twisted.trial import unittest 11 11 … … 148 148 client.connect(('127.0.0.1', self.serverSocket.getsockname()[1])) 149 149 except socket.error, e: 150 150 self.assertEquals(e.args[0], errno.EINPROGRESS) 151 else: 152 raise unittest.FailTest("Connect should have raised EINPROGRESS") 151 153 server, addr = self.serverSocket.accept() 152 154 153 155 self.connections.extend((client, server)) … … 342 344 timer.addToLoop(0.01) 343 345 self.assertRaises(RuntimeError, newEventBase.loop, libevent.EVLOOP_ONCE) 344 346 345 346 347 def test_dispatchError(self): 347 348 """ 348 349 Check that dispatch forwards exception raised in callback. … … 367 368 newEventBase.dispatch() 368 369 self.assertEquals(len(fireEvents), 1) 369 370 370 371 371 def test_successfulCallbackReference(self): 372 372 """ 373 373 Check that successful callbacks aren't leaked. … … 375 375 newEventBase = libevent.EventBase() 376 376 def cb(fd, events, obj): 377 377 pass 378 import sys379 378 self._watchForLeaks(cb) 380 379 timer = newEventBase.createTimer(cb) 381 380 timer.addToLoop(0.002) … … 383 382 384 383 del cb, timer 385 384 self._assertLeaks() 386 387 385 388 386 def test_failedCallbackReference(self): 389 387 """ … … 400 398 del eb, timer 401 399 self._assertLeaks() 402 400 403 404 401 def test_unfiredCallbackReference(self): 405 402 """ 406 403 Check that unfired callbacks aren't leaked when the eventBase is … … 410 407 def cb(fd, events, obj): 411 408 pass 412 409 self._watchForLeaks(cb) 410 #print map(sys.getrefcount, [newEventBase, cb]) 413 411 timer = newEventBase.createTimer(cb) 412 #print map(sys.getrefcount, [newEventBase, timer, cb]) 414 413 timer.addToLoop(1) 414 #print map(sys.getrefcount, [newEventBase, timer, cb]) 415 415 416 416 del cb, timer, newEventBase 417 417 self._assertLeaks() 418 419 418 420 419 def test_callbackReference(self): 421 420 """ … … 429 428 430 429 del cb, timer 431 430 self._assertLeaks() 432 433 431 434 432 def test_callbackExceptionReference(self): 435 433 """ … … 449 447 self._watchForLeaks(exc[0]) 450 448 451 449 del exc[0] 452 453 450 self._assertLeaks() 454 455 451 456 452 def test_callbackSurvival(self): 457 453 """ … … 459 455 dies. 460 456 """ 461 457 newEventBase = libevent.EventBase() 462 fireEvents = []463 458 def cb(fd, events, obj): 464 fireEvents.append((fd, events, obj))459 pass 465 460 timer = newEventBase.createTimer(cb) 466 461 timer.addToLoop(1) 467 462 self._watchForSurvival(cb) … … 469 464 del cb, timer 470 465 self._assertSurvival() 471 466 467 def test_persistentCallbackSurvival(self): 468 """ 469 Check that a persistent callback survives after been fired. 470 """ 471 rfd, wfd = os.pipe() 472 newEventBase = libevent.EventBase() 473 def cb(fd, events, obj): 474 newEventBase.loopExit(0) 475 timer = newEventBase.createEvent(rfd, 476 libevent.EV_READ | libevent.EV_PERSIST, cb) 477 timer.addToLoop() 478 os.write(wfd, " ") 479 newEventBase.dispatch() 480 self._watchForSurvival(cb) 481 482 del cb, timer 483 self._assertSurvival() 484 485 def test_persistentFailedCallbackSurvival(self): 486 """ 487 Check that a persistent callback survives after raising an exception. 488 """ 489 rfd, wfd = os.pipe() 490 newEventBase = libevent.EventBase() 491 def cb(fd, events, obj): 492 newEventBase.loopExit(0) 493 raise RuntimeError("foo") 494 timer = newEventBase.createEvent(rfd, 495 libevent.EV_READ | libevent.EV_PERSIST, cb) 496 timer.addToLoop() 497 os.write(wfd, " ") 498 self.assertRaises(RuntimeError, newEventBase.dispatch) 499 self._watchForSurvival(cb) 500 501 del cb, timer 502 self._assertSurvival() 503 504 def test_persistentCallbackReference(self): 505 """ 506 Check that a persistent callback doesn't leak when the eventBase 507 is destroyed. 508 """ 509 rfd, wfd = os.pipe() 510 newEventBase = libevent.EventBase() 511 def cb(fd, events, obj): 512 newEventBase.loopExit(0) 513 timer = newEventBase.createEvent(rfd, 514 libevent.EV_READ | libevent.EV_PERSIST, cb) 515 timer.addToLoop() 516 os.write(wfd, " ") 517 newEventBase.dispatch() 518 self._watchForLeaks(cb) 519 520 newEventBase = None 521 del cb, timer 522 self._assertLeaks() 472 523 473 524 def test_dispatchedEventRefCount(self): 474 525 """
