Skip to content

Commit a86d4fb

Browse files
committed
Cleanup and avoid refleaks OSX Timer__timer_start.
Reference leaks where occurring with early `return NULL`, e.g. if `PyObject_IsTrue(attribute)` or `PyMethod_Check(attribute)` failed. Also there's plenty of cases where one doesn't need to set the error message, as PyObject_GetAttrString (for example) will set the correct exception. Also apply similar cleanups to drawRect, even though it doesn't actually suffer from refleaks.
1 parent c292827 commit a86d4fb

File tree

2 files changed

+27
-59
lines changed

2 files changed

+27
-59
lines changed

lib/matplotlib/backends/backend_macosx.py

-2
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,8 @@ def _set_device_scale(self, value):
5555

5656
def _draw(self):
5757
renderer = self.get_renderer(cleared=self.figure.stale)
58-
5958
if self.figure.stale:
6059
self.figure.draw(renderer)
61-
6260
return renderer
6361

6462
def draw(self):

src/_macosx.m

+27-57
Original file line numberDiff line numberDiff line change
@@ -1672,26 +1672,15 @@ -(void)drawRect:(NSRect)rect
16721672
goto exit;
16731673
}
16741674
}
1675-
1676-
renderer = PyObject_CallMethod(canvas, "_draw", "", NULL);
1677-
if (!renderer)
1678-
{
1675+
if (!(renderer = PyObject_CallMethod(canvas, "_draw", "", NULL))
1676+
|| !(renderer_buffer = PyObject_GetAttrString(renderer, "_renderer"))) {
16791677
PyErr_Print();
16801678
goto exit;
16811679
}
1682-
1683-
renderer_buffer = PyObject_GetAttrString(renderer, "_renderer");
1684-
if (!renderer_buffer) {
1685-
PyErr_Print();
1686-
goto exit;
1687-
}
1688-
16891680
if (_copy_agg_buffer(cr, renderer_buffer)) {
16901681
printf("copy_agg_buffer failed\n");
16911682
goto exit;
16921683
}
1693-
1694-
16951684
if (!NSIsEmptyRect(rubberband)) {
16961685
NSFrameRect(rubberband);
16971686
}
@@ -2391,63 +2380,37 @@ static void context_cleanup(const void* info)
23912380
CFRunLoopRef runloop;
23922381
CFRunLoopTimerRef timer;
23932382
CFRunLoopTimerContext context;
2394-
double milliseconds;
23952383
CFAbsoluteTime firstFire;
23962384
CFTimeInterval interval;
2397-
PyObject* attribute;
2398-
PyObject* failure;
2385+
PyObject* py_interval = NULL, * py_single = NULL, * py_on_timer = NULL;
2386+
int single;
23992387
runloop = CFRunLoopGetCurrent();
24002388
if (!runloop) {
24012389
PyErr_SetString(PyExc_RuntimeError, "Failed to obtain run loop");
24022390
return NULL;
24032391
}
2404-
attribute = PyObject_GetAttrString((PyObject*)self, "_interval");
2405-
if (attribute==NULL)
2406-
{
2407-
PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_interval'");
2408-
return NULL;
2409-
}
2410-
milliseconds = PyFloat_AsDouble(attribute);
2411-
failure = PyErr_Occurred();
2412-
Py_DECREF(attribute);
2413-
if (failure) return NULL;
2414-
attribute = PyObject_GetAttrString((PyObject*)self, "_single");
2415-
if (attribute==NULL)
2416-
{
2417-
PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_single'");
2418-
return NULL;
2392+
if (!(py_interval = PyObject_GetAttrString((PyObject*)self, "_interval"))
2393+
|| ((interval = PyFloat_AsDouble(py_interval) / 1000.), PyErr_Occurred())
2394+
|| !(py_single = PyObject_GetAttrString((PyObject*)self, "_single"))
2395+
|| ((single = PyObject_IsTrue(py_single)) == -1)
2396+
|| !(py_on_timer = PyObject_GetAttrString((PyObject*)self, "_on_timer"))) {
2397+
goto exit;
24192398
}
2420-
// Need to tell when to first fire this timer, so get the current time
2421-
// and add an interval.
2422-
interval = milliseconds / 1000.0;
2399+
// (current time + interval) is time of first fire.
24232400
firstFire = CFAbsoluteTimeGetCurrent() + interval;
2424-
switch (PyObject_IsTrue(attribute)) {
2425-
case 1:
2426-
interval = 0;
2427-
break;
2428-
case 0: // Set by default above
2429-
break;
2430-
case -1:
2431-
default:
2432-
PyErr_SetString(PyExc_ValueError, "Cannot interpret _single attribute as True of False");
2433-
return NULL;
2434-
}
2435-
Py_DECREF(attribute);
2436-
attribute = PyObject_GetAttrString((PyObject*)self, "_on_timer");
2437-
if (attribute==NULL)
2438-
{
2439-
PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_on_timer'");
2440-
return NULL;
2401+
if (single) {
2402+
interval = 0;
24412403
}
2442-
if (!PyMethod_Check(attribute)) {
2404+
if (!PyMethod_Check(py_on_timer)) {
24432405
PyErr_SetString(PyExc_RuntimeError, "_on_timer should be a Python method");
2444-
return NULL;
2406+
goto exit;
24452407
}
2408+
Py_INCREF(py_on_timer);
24462409
context.version = 0;
24472410
context.retain = NULL;
24482411
context.release = context_cleanup;
24492412
context.copyDescription = NULL;
2450-
context.info = attribute;
2413+
context.info = py_on_timer;
24512414
timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
24522415
firstFire,
24532416
interval,
@@ -2456,9 +2419,8 @@ static void context_cleanup(const void* info)
24562419
timer_callback,
24572420
&context);
24582421
if (!timer) {
2459-
Py_DECREF(attribute);
24602422
PyErr_SetString(PyExc_RuntimeError, "Failed to create timer");
2461-
return NULL;
2423+
goto exit;
24622424
}
24632425
if (self->timer) {
24642426
CFRunLoopTimerInvalidate(self->timer);
@@ -2469,7 +2431,15 @@ static void context_cleanup(const void* info)
24692431
* the timer lost before we have a chance to decrease the reference count
24702432
* of the attribute */
24712433
self->timer = timer;
2472-
Py_RETURN_NONE;
2434+
exit:
2435+
Py_XDECREF(py_interval);
2436+
Py_XDECREF(py_single);
2437+
Py_XDECREF(py_on_timer);
2438+
if (PyErr_Occurred()) {
2439+
return NULL;
2440+
} else {
2441+
Py_RETURN_NONE;
2442+
}
24732443
}
24742444

24752445
static PyObject*

0 commit comments

Comments
 (0)