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
This commit is contained in:
Vadim Yanitskiy 2019-11-24 01:30:57 +07:00
parent ae8e5ad648
commit baf07c4be2
2 changed files with 41 additions and 22 deletions

View File

@ -4,7 +4,7 @@
# TRX Toolkit # TRX Toolkit
# Simple TDMA frame clock generator # Simple TDMA frame clock generator
# #
# (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com> # (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
# #
# All Rights Reserved # All Rights Reserved
# #
@ -22,13 +22,14 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy <axilirator@gmail.com>")] APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")]
import logging as log import logging as log
import threading
import signal import signal
import time
from app_common import ApplicationBase from app_common import ApplicationBase
from threading import Timer
from udp_link import UDPLink from udp_link import UDPLink
from gsm_shared import * from gsm_shared import *
@ -40,32 +41,54 @@ class CLCKGen:
# Average loop back delay # Average loop back delay
LO_DELAY_US = 90.0 LO_DELAY_US = 90.0
# State variables
timer = None
def __init__(self, clck_links, clck_start = 0, ind_period = 102): 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.clck_links = clck_links
self.ind_period = ind_period self.ind_period = ind_period
self.clck_start = clck_start self.clck_start = clck_start
self.clck_src = clck_start
# Calculate counter time # Calculate counter time
self.ctr_interval = self.GSM_FRAME_US - self.LO_DELAY_US self.ctr_interval = self.GSM_FRAME_US - self.LO_DELAY_US
self.ctr_interval /= self.SEC_DELAY_US self.ctr_interval /= self.SEC_DELAY_US
self.ctr_interval *= self.ind_period self.ctr_interval *= self.ind_period
@property
def running(self):
if self._thread is None:
return False
return self._thread.isAlive()
def start(self): def start(self):
# Send the first indication # Make sure we won't start two threads
self.send_clck_ind() 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): def stop(self):
# Stop pending timer # No thread, no problem ;)
if self.timer is not None: if self._thread is None:
self.timer.cancel() return
self.timer = None
# Reset the clock source # Stop the thread first
self.clck_src = self.clck_start 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): def send_clck_ind(self):
# Keep clock cycle # Keep clock cycle
@ -87,10 +110,6 @@ class CLCKGen:
# Increase frame count # Increase frame count
self.clck_src += self.ind_period 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 # Just a wrapper for independent usage
class Application(ApplicationBase): class Application(ApplicationBase):
def __init__(self): def __init__(self):
@ -102,7 +121,7 @@ class Application(ApplicationBase):
# Configure logging # Configure logging
log.basicConfig(level = log.DEBUG, 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): def run(self):
self.link = UDPLink("127.0.0.1", 5800, "0.0.0.0", 5700) self.link = UDPLink("127.0.0.1", 5800, "0.0.0.0", 5700)

View File

@ -172,10 +172,10 @@ class Transceiver:
# Transceiver was started # Transceiver was started
clck_links.append(self.clck_if) 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") log.info("Starting clock generator")
self.clck_gen.start() 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") log.info("Stopping clock generator")
self.clck_gen.stop() self.clck_gen.stop()