3030
3131
3232_LOGGER = logging .getLogger (__name__ )
33- _CALLBACK_WORKER_NAME = 'CallbackRequestsWorker'
33+ _CALLBACK_WORKER_NAME = 'Thread-Consumer- CallbackRequestsWorker'
3434
3535
3636def _callback_completed (future ):
@@ -98,6 +98,9 @@ def __init__(self, client, subscription, flow_control=types.FlowControl(),
9898 self ._request_queue = self ._get_queue (queue )
9999 # Also maintain an executor.
100100 self ._executor = self ._get_executor (executor )
101+ # The threads created in ``.open()``.
102+ self ._dispatch_thread = None
103+ self ._leases_thread = None
101104
102105 @staticmethod
103106 def _get_queue (queue ):
@@ -146,8 +149,12 @@ def _get_executor(executor):
146149 def close (self ):
147150 """Close the existing connection."""
148151 # Stop consuming messages.
149- self ._consumer .helper_threads .stop (_CALLBACK_WORKER_NAME )
152+ self ._request_queue .put (_helper_threads .STOP )
153+ self ._dispatch_thread .join () # Wait until stopped.
154+ self ._dispatch_thread = None
150155 self ._consumer .stop_consuming ()
156+ self ._leases_thread .join ()
157+ self ._leases_thread = None
151158 self ._executor .shutdown ()
152159
153160 # The subscription is closing cleanly; resolve the future if it is not
@@ -156,6 +163,53 @@ def close(self):
156163 self ._future .set_result (None )
157164 self ._future = None
158165
166+ def _start_dispatch (self ):
167+ """Start a thread to dispatch requests queued up by callbacks.
168+
169+ .. note::
170+
171+ This assumes, but does not check, that ``_dispatch_thread``
172+ is :data:`None`.
173+
174+ Spawns a thread to run :meth:`dispatch_callback` and sets the
175+ "dispatch thread" member on the current policy.
176+ """
177+ _LOGGER .debug ('Starting callback requests worker.' )
178+ dispatch_worker = _helper_threads .QueueCallbackWorker (
179+ self ._request_queue ,
180+ self .dispatch_callback ,
181+ )
182+ # Create and start the helper thread.
183+ thread = threading .Thread (
184+ name = _CALLBACK_WORKER_NAME ,
185+ target = dispatch_worker ,
186+ )
187+ thread .daemon = True
188+ thread .start ()
189+ _LOGGER .debug ('Started helper thread %s' , thread .name )
190+ self ._dispatch_thread = thread
191+
192+ def _start_lease_worker (self ):
193+ """Spawn a helper thread that maintains all of leases for this policy.
194+
195+ .. note::
196+
197+ This assumes, but does not check, that ``_leases_thread`` is
198+ :data:`None`.
199+
200+ Spawns a thread to run :meth:`maintain_leases` and sets the
201+ "leases thread" member on the current policy.
202+ """
203+ _LOGGER .debug ('Starting lease maintenance worker.' )
204+ thread = threading .Thread (
205+ name = 'Thread-LeaseMaintenance' ,
206+ target = self .maintain_leases ,
207+ )
208+ thread .daemon = True
209+ thread .start ()
210+
211+ self ._leases_thread = thread
212+
159213 def open (self , callback ):
160214 """Open a streaming pull connection and begin receiving messages.
161215
@@ -177,30 +231,11 @@ def open(self, callback):
177231 self ._future = Future (policy = self )
178232
179233 # Start the thread to pass the requests.
180- _LOGGER .debug ('Starting callback requests worker.' )
181234 self ._callback = callback
182- dispatch_worker = _helper_threads .QueueCallbackWorker (
183- self ._request_queue ,
184- self .dispatch_callback ,
185- )
186- self ._consumer .helper_threads .start (
187- _CALLBACK_WORKER_NAME ,
188- self ._request_queue .put ,
189- dispatch_worker ,
190- )
191-
235+ self ._start_dispatch ()
192236 # Actually start consuming messages.
193237 self ._consumer .start_consuming ()
194-
195- # Spawn a helper thread that maintains all of the leases for
196- # this policy.
197- _LOGGER .debug ('Starting lease maintenance worker.' )
198- self ._leaser = threading .Thread (
199- name = 'Thread-LeaseMaintenance' ,
200- target = self .maintain_leases ,
201- )
202- self ._leaser .daemon = True
203- self ._leaser .start ()
238+ self ._start_lease_worker ()
204239
205240 # Return the future.
206241 return self ._future
0 commit comments