Skip to content

Commit e83b987

Browse files
committed
DISPATCH-1962 Fix leak of IoAdapter_init
1 parent 8147b62 commit e83b987

File tree

7 files changed

+60
-13
lines changed

7 files changed

+60
-13
lines changed

.travis.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ jobs:
7575
- sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main' -y
7676
- sudo apt-get update -q
7777
- sudo apt-get install -y clang-13 llvm-13-dev
78+
# Python 3.8.2 in Ubuntu LTS has ASan issues
79+
- sudo add-apt-repository -y ppa:deadsnakes/ppa
80+
- sudo apt-get update
81+
- sudo apt-get install python3.9 python3.9-dev python3.9-distutils
82+
- sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.9 10
83+
- sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 10
7884
# Update pip, it may prevent issues later
7985
- sudo apt-get install -y python3-pip
8086
- python3 -m pip install --user --upgrade pip
@@ -124,6 +130,13 @@ jobs:
124130
compiler: clang
125131
before_install:
126132
- sudo apt-get install clang-12 llvm-12-dev
133+
# Python 3.8.2 in Ubuntu LTS has ASan issues
134+
- sudo apt install -y python3-pip
135+
- sudo add-apt-repository -y ppa:deadsnakes/ppa
136+
- sudo apt-get update
137+
- sudo apt-get install python3.9 python3.9-dev python3.9-distutils
138+
- sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.9 10
139+
- sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 10
127140
# Install and use the latest Node.js LTS version
128141
- nvm install "lts/*"
129142
# Update pip, it may prevent issues later
@@ -152,6 +165,11 @@ jobs:
152165
os: linux
153166
dist: focal
154167
before_install:
168+
- sudo add-apt-repository -y ppa:deadsnakes/ppa
169+
- sudo apt-get update
170+
- sudo apt-get install python3.9 python3.9-dev python3.9-distutils
171+
- sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.9 10
172+
- sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 10
155173
# Update pip, it may prevent issues later
156174
- sudo apt-get install -y python3-pip
157175
- python3 -m pip install --user --upgrade pip

src/dispatch.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ qd_error_t qd_dispatch_prepare(qd_dispatch_t *qd)
340340
void qd_dispatch_set_agent(qd_dispatch_t *qd, void *agent) {
341341
assert(agent);
342342
assert(!qd->agent);
343+
Py_IncRef(agent);
343344
qd->agent = agent;
344345
}
345346

src/python_embedded.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,8 @@ static PyTypeObject LogAdapterType = {
565565
.tp_dealloc = (destructor)LogAdapter_dealloc,
566566
.tp_flags = Py_TPFLAGS_DEFAULT,
567567
.tp_methods = LogAdapter_methods,
568-
.tp_init = (initproc)LogAdapter_init
568+
.tp_init = (initproc)LogAdapter_init,
569+
.tp_new = PyType_GenericNew,
569570
};
570571

571572

@@ -719,10 +720,24 @@ static int IoAdapter_init(IoAdapter *self, PyObject *args, PyObject *kwds)
719720
return 0;
720721
}
721722

723+
// visit all members which may conceivably participate in reference cycles
724+
static int IoAdapter_traverse(IoAdapter* self, visitproc visit, void *arg)
725+
{
726+
Py_VISIT(self->handler);
727+
return 0;
728+
}
729+
730+
static int IoAdapter_clear(IoAdapter* self)
731+
{
732+
Py_CLEAR(self->handler);
733+
return 0;
734+
}
735+
722736
static void IoAdapter_dealloc(IoAdapter* self)
723737
{
724738
qdr_core_unsubscribe(self->sub);
725-
Py_DECREF(self->handler);
739+
PyObject_GC_UnTrack(self);
740+
IoAdapter_clear(self);
726741
Py_TYPE(self)->tp_free((PyObject*)self);
727742
}
728743

@@ -815,10 +830,13 @@ static PyTypeObject IoAdapterType = {
815830
.tp_name = DISPATCH_MODULE ".IoAdapter",
816831
.tp_doc = "Dispatch IO Adapter",
817832
.tp_basicsize = sizeof(IoAdapter),
833+
.tp_traverse = (traverseproc)IoAdapter_traverse,
834+
.tp_clear = (inquiry)IoAdapter_clear,
818835
.tp_dealloc = (destructor)IoAdapter_dealloc,
819-
.tp_flags = Py_TPFLAGS_DEFAULT,
836+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
820837
.tp_methods = IoAdapter_methods,
821838
.tp_init = (initproc)IoAdapter_init,
839+
.tp_new = PyType_GenericNew,
822840
};
823841

824842

@@ -834,8 +852,6 @@ static void qd_register_constant(PyObject *module, const char *name, uint32_t va
834852

835853
static void qd_python_setup(void)
836854
{
837-
LogAdapterType.tp_new = PyType_GenericNew;
838-
IoAdapterType.tp_new = PyType_GenericNew;
839855
if ((PyType_Ready(&LogAdapterType) < 0) || (PyType_Ready(&IoAdapterType) < 0)) {
840856
qd_error_py();
841857
qd_log(log_source, QD_LOG_CRITICAL, "Unable to initialize Adapters");

src/router_node.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2161,12 +2161,12 @@ void qd_router_free(qd_router_t *router)
21612161

21622162
qd_container_set_default_node_type(router->qd, 0, 0, QD_DIST_BOTH);
21632163

2164+
qd_router_python_free(router);
21642165
qdr_core_free(router->router_core);
21652166
qd_tracemask_free(router->tracemask);
21662167
qd_timer_free(router->timer);
21672168
sys_mutex_free(router->lock);
21682169
qd_router_configure_free(router);
2169-
qd_router_python_free(router);
21702170

21712171
free(router);
21722172
free(node_id);

src/router_pynode.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ qd_error_t qd_router_python_setup(qd_router_t *router)
443443
// Instantiate the router
444444
//
445445
pyRouter = PyObject_CallObject(pClass, pArgs);
446+
Py_DECREF(pClass);
446447
Py_DECREF(pArgs);
447448
Py_DECREF(adapterType);
448449
QD_ERROR_PY_RET();
@@ -455,7 +456,12 @@ qd_error_t qd_router_python_setup(qd_router_t *router)
455456
}
456457

457458
void qd_router_python_free(qd_router_t *router) {
458-
// empty
459+
Py_XDECREF(pyRouter);
460+
Py_XDECREF(pyTick);
461+
Py_XDECREF(pySetMobileSeq);
462+
Py_XDECREF(pySetMyMobileSeq);
463+
Py_XDECREF(pyLinkLost);
464+
PyGC_Collect();
459465
}
460466

461467

tests/lsan.supp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
# found by AddressSanitizer (ASAN)
33
#
44

5-
# to be triaged; pretty much all tests
6-
leak:^IoAdapter_init$
7-
85
# to be triaged; pretty much all tests
96
leak:^load_server_config$
107

tests/system_test.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import shutil
4040
import socket
4141
import subprocess
42+
import sys
43+
import time
4244
from copy import copy
4345
from datetime import datetime
4446
from subprocess import PIPE, STDOUT
@@ -191,10 +193,13 @@ def port_available(port, protocol_family='IPv4'):
191193
def wait_port(port, protocol_family='IPv4', **retry_kwargs):
192194
"""Wait up to timeout for port (on host) to be connectable.
193195
Takes same keyword arguments as retry to control the timeout"""
196+
194197
def check(e):
195198
"""Only retry on connection refused"""
196-
if not isinstance(e, socket.error) or not e.errno == errno.ECONNREFUSED:
197-
raise
199+
# TODO(DISPATCH-1539): in Python 3.3+ it is sufficient to catch only OSError
200+
if isinstance(e, (socket.error, IOError, OSError)) and e.errno == errno.ECONNREFUSED:
201+
return
202+
raise
198203

199204
host = None
200205

@@ -346,7 +351,11 @@ def __init__(self, name=None, listen_port=None, wait=True,
346351
self.args += self.cl_args
347352
super(Http2Server, self).__init__(self.args, name=name, expect=expect)
348353
if wait:
349-
self.wait_ready()
354+
try:
355+
self.wait_ready()
356+
except Exception:
357+
self.teardown()
358+
raise
350359

351360
def wait_ready(self, **retry_kwargs):
352361
"""

0 commit comments

Comments
 (0)