aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/python/grpcio/grpc/_common.py
diff options
context:
space:
mode:
authorGravatar Ken Payson <kpayson@google.com>2016-06-10 13:19:31 -0700
committerGravatar Ken Payson <kpayson@google.com>2016-06-13 15:38:53 -0700
commited7428526cea1f7b54a126ef9cdb2c1456c47f2c (patch)
tree64d4239e374dbbc2da4be8153db816c29b5a4350 /src/python/grpcio/grpc/_common.py
parent68e5ecbee4882a74b16a6e22d935d68798016804 (diff)
Added cleanup to the server thread's join method.
When the Python Interpreter exits, it first attempts to join any outstanding threads. This is problematic if a server is created as a top-level variable or referenced by a reference cycle, as join() will hang. This adds cleanup behavior to the server thread's join().
Diffstat (limited to 'src/python/grpcio/grpc/_common.py')
-rw-r--r--src/python/grpcio/grpc/_common.py42
1 files changed, 42 insertions, 0 deletions
diff --git a/src/python/grpcio/grpc/_common.py b/src/python/grpcio/grpc/_common.py
index b8688a0149..1fd1704f18 100644
--- a/src/python/grpcio/grpc/_common.py
+++ b/src/python/grpcio/grpc/_common.py
@@ -30,6 +30,8 @@
"""Shared implementation."""
import logging
+import threading
+import time
import six
@@ -110,3 +112,43 @@ def fully_qualified_method(group, method):
group = _encode(group)
method = _encode(method)
return b'/' + group + b'/' + method
+
+
+class CleanupThread(threading.Thread):
+ """A threading.Thread subclass supporting custom behavior on join().
+
+ On Python Interpreter exit, Python will attempt to join outstanding threads
+ prior to garbage collection. We may need to do additional cleanup, and
+ we accomplish this by overriding the join() method.
+ """
+
+ def __init__(self, behavior, group=None, target=None, name=None,
+ args=(), kwargs={}):
+ """Constructor.
+
+ Args:
+ behavior (function): Function called on join() with a single
+ argument, timeout, indicating the maximum duration of
+ `behavior`, or None indicating `behavior` has no deadline.
+ `behavior` must be idempotent.
+ group (None): should be None. Reseved for future extensions
+ when ThreadGroup is implemented.
+ target (function): The function to invoke when this thread is
+ run. Defaults to None.
+ name (str): The name of this thread. Defaults to None.
+ args (tuple[object]): A tuple of arguments to pass to `target`.
+ kwargs (dict[str,object]): A dictionary of keyword arguments to
+ pass to `target`.
+ """
+ super(CleanupThread, self).__init__(group=group, target=target,
+ name=name, args=args, kwargs=kwargs)
+ self._behavior = behavior
+
+ def join(self, timeout=None):
+ start_time = time.time()
+ self._behavior(timeout)
+ end_time = time.time()
+ if timeout is not None:
+ timeout -= end_time - start_time
+ timeout = max(timeout, 0)
+ super(CleanupThread, self).join(timeout)