Skip to content

Commit fea501f

Browse files
committed
PYTHON-525 Try to halt monitors before exiting.
1 parent cfcc601 commit fea501f

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

pymongo/monitor.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def open(self):
6767
thread = threading.Thread(target=self.run)
6868
thread.daemon = True
6969
self._thread = weakref.proxy(thread)
70+
register_monitor(self)
7071
thread.start()
7172

7273
def close(self):
@@ -171,6 +172,11 @@ def _check_with_socket(self, sock_info):
171172
return IsMaster(result['data'][0]), time.time() - start
172173

173174

175+
# MONITORS has a weakref to each running Monitor. A Monitor is kept alive by
176+
# a strong reference from its Server and its Thread. Once both are destroyed
177+
# the Monitor is garbage-collected and removed from MONITORS. If, however,
178+
# any threads are still running when the interpreter begins to shut down,
179+
# we attempt to halt and join them to avoid spurious errors.
174180
MONITORS = set()
175181

176182

@@ -192,6 +198,6 @@ def shutdown_monitors():
192198
monitor = ref()
193199
if monitor:
194200
monitor.close()
195-
monitor.join()
201+
monitor.join(10)
196202

197203
atexit.register(shutdown_monitors)

test/test_monitor.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2014 MongoDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Test the monitor module."""
16+
17+
import gc
18+
import sys
19+
20+
sys.path[0:0] = [""]
21+
22+
from pymongo.monitor import MONITORS
23+
from test import unittest, port, host, IntegrationTest
24+
from test.utils import get_client, wait_until
25+
26+
27+
class TestMonitor(IntegrationTest):
28+
def test_atexit_hook(self):
29+
n_monitors = len(MONITORS)
30+
client = get_client(host, port)
31+
wait_until(lambda: len(MONITORS) == n_monitors + 1,
32+
'register new monitor')
33+
34+
del client
35+
gc.collect()
36+
37+
wait_until(lambda: len(MONITORS) == n_monitors,
38+
'unregister monitor')
39+
40+
41+
if __name__ == "__main__":
42+
unittest.main()

0 commit comments

Comments
 (0)