stoppable_thread.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. # Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD
  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. import threading
  15. from typing import Optional
  16. class StoppableThread(object):
  17. """
  18. Provide a Thread-like class which can be 'cancelled' via a subclass-provided
  19. cancellation method.
  20. Can be started and stopped multiple times.
  21. Isn't an instance of type Thread because Python Thread objects can only be run once
  22. """
  23. def __init__(self):
  24. # type: () -> None
  25. self._thread = None # type: Optional[threading.Thread]
  26. @property
  27. def alive(self):
  28. # type: () -> bool
  29. """
  30. Is 'alive' whenever the internal thread object exists
  31. """
  32. return self._thread is not None
  33. def start(self):
  34. # type: () -> None
  35. if self._thread is None:
  36. self._thread = threading.Thread(target=self._run_outer)
  37. self._thread.start()
  38. def _cancel(self):
  39. # type: () -> None
  40. pass # override to provide cancellation functionality
  41. def run(self):
  42. # type: () -> None
  43. pass # override for the main thread behaviour
  44. def _run_outer(self):
  45. # type: () -> None
  46. try:
  47. self.run()
  48. finally:
  49. self._thread = None
  50. def stop(self):
  51. # type: () -> None
  52. if self._thread is not None:
  53. old_thread = self._thread
  54. self._thread = None
  55. self._cancel()
  56. old_thread.join()