From baf07c4be2faf791b4c5d34d065eafea1408e95a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 24 Nov 2019 01:30:57 +0700 Subject: [PATCH] trx_toolkit/clck_gen.py: refactor CLCKGen to use a single thread The previous approach was based on threading.Timer, so on each clock iteration one thread spawned another new thread. So far it worked well, but such frequent spawning involves an additional overhead. After this change, CLCKGen.start() allocates and starts a new thread, that periodically sends clock indications and sleep()s during the indication intervals. The CLCKGen.stop() in its turn terminates that thread and frees the memory. Change-Id: Ibe477eb0a1ee2193c1ff16452a407be7e858b2ef --- src/target/trx_toolkit/clck_gen.py | 59 ++++++++++++++++++--------- src/target/trx_toolkit/transceiver.py | 4 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py index c58d8bde2..92ca217c5 100755 --- a/src/target/trx_toolkit/clck_gen.py +++ b/src/target/trx_toolkit/clck_gen.py @@ -4,7 +4,7 @@ # TRX Toolkit # Simple TDMA frame clock generator # -# (C) 2017-2018 by Vadim Yanitskiy +# (C) 2017-2019 by Vadim Yanitskiy # # All Rights Reserved # @@ -22,13 +22,14 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] +APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy ")] import logging as log +import threading import signal +import time from app_common import ApplicationBase -from threading import Timer from udp_link import UDPLink from gsm_shared import * @@ -40,32 +41,54 @@ class CLCKGen: # Average loop back delay LO_DELAY_US = 90.0 - # State variables - timer = None - def __init__(self, clck_links, clck_start = 0, ind_period = 102): + # This event is needed to control the thread + self._breaker = threading.Event() + self._thread = None + self.clck_links = clck_links self.ind_period = ind_period self.clck_start = clck_start - self.clck_src = clck_start # Calculate counter time self.ctr_interval = self.GSM_FRAME_US - self.LO_DELAY_US self.ctr_interval /= self.SEC_DELAY_US self.ctr_interval *= self.ind_period + @property + def running(self): + if self._thread is None: + return False + return self._thread.isAlive() + def start(self): - # Send the first indication - self.send_clck_ind() + # Make sure we won't start two threads + assert(self._thread is None) + + # (Re)set the clock counter + self.clck_src = self.clck_start + + # Initialize and start a new thread + self._thread = threading.Thread(target = self._worker) + self._thread.start() def stop(self): - # Stop pending timer - if self.timer is not None: - self.timer.cancel() - self.timer = None + # No thread, no problem ;) + if self._thread is None: + return - # Reset the clock source - self.clck_src = self.clck_start + # Stop the thread first + self._breaker.set() + self._thread.join() + + # Free memory, reset breaker + del self._thread + self._thread = None + self._breaker.clear() + + def _worker(self): + while not self._breaker.wait(self.ctr_interval): + self.send_clck_ind() def send_clck_ind(self): # Keep clock cycle @@ -87,10 +110,6 @@ class CLCKGen: # Increase frame count self.clck_src += self.ind_period - # Schedule a new indication - self.timer = Timer(self.ctr_interval, self.send_clck_ind) - self.timer.start() - # Just a wrapper for independent usage class Application(ApplicationBase): def __init__(self): @@ -102,7 +121,7 @@ class Application(ApplicationBase): # Configure logging log.basicConfig(level = log.DEBUG, - format = "[%(levelname)s] %(filename)s:%(lineno)d %(message)s") + format = "[%(levelname)s] TID#%(thread)s %(filename)s:%(lineno)d %(message)s") def run(self): self.link = UDPLink("127.0.0.1", 5800, "0.0.0.0", 5700) diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py index 37680e752..b1a5c117d 100644 --- a/src/target/trx_toolkit/transceiver.py +++ b/src/target/trx_toolkit/transceiver.py @@ -172,10 +172,10 @@ class Transceiver: # Transceiver was started clck_links.append(self.clck_if) - if not self.clck_gen.timer and len(clck_links) > 0: + if not self.clck_gen.running and len(clck_links) > 0: log.info("Starting clock generator") self.clck_gen.start() - elif self.clck_gen.timer and not clck_links: + elif self.clck_gen.running and not clck_links: log.info("Stopping clock generator") self.clck_gen.stop()