Add kill flag to AprsClient

When starting an AprsClient with AprsClient.run(...) the client enters
a loop without an exit condition (i.e. a while True loop).  If autoreconnect
is set to True, it is impossible to exit the aforementioned loop even if
AprsClient.disconnect() is called.

This causes problems when running the client in a thread (or as a
background service, etc.) as the process will not join nor terminate
unless explicitly shutdown with SIGKILL.

Minimal example:

```
import signal

from ogn.client import AprsClient
from ogn.parser import parse_aprs, parse_ogn_beacon, ParseError

def process_beacon(raw_message):
    print('Received message')

client = AprsClient(aprs_user='N0CALL')
signal.signal(signal.SIGTERM, lambda signo, stackno: client.disconnect())
client.connect()
client.run(callback=process_beacon, autoreconnect=True)
```

This commit fixes such issues by adding a kill flag that is raised when
calling AprsClien.disconnect() to the while conditions of both loops inside
AprsClient.run().

Note: the outermost loop could still remain a while True loop as the
exit condition is checked at the end of the loop body.
pull/26/head
Anze Kolar 2017-07-20 10:58:28 +02:00
rodzic d0044deb47
commit 182f9518a4
2 zmienionych plików z 8 dodań i 3 usunięć

Wyświetl plik

@ -19,6 +19,8 @@ class AprsClient:
self.aprs_user = aprs_user self.aprs_user = aprs_user
self.aprs_filter = aprs_filter self.aprs_filter = aprs_filter
self._kill = False
def connect(self): def connect(self):
# create socket, connect to server, login and make a file object associated with the socket # create socket, connect to server, login and make a file object associated with the socket
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -45,11 +47,13 @@ class AprsClient:
except OSError: except OSError:
self.logger.error('Socket close error', exc_info=True) self.logger.error('Socket close error', exc_info=True)
self._kill = True
def run(self, callback, timed_callback=lambda client: None, autoreconnect=False): def run(self, callback, timed_callback=lambda client: None, autoreconnect=False):
while True: while not self._kill:
try: try:
keepalive_time = time() keepalive_time = time()
while True: while not self._kill:
if time() - keepalive_time > settings.APRS_KEEPALIVE_TIME: if time() - keepalive_time > settings.APRS_KEEPALIVE_TIME:
self.logger.info('Send keepalive') self.logger.info('Send keepalive')
self.sock.send('#keepalive\n'.encode()) self.sock.send('#keepalive\n'.encode())
@ -71,7 +75,7 @@ class AprsClient:
except socket.error: except socket.error:
self.logger.error('socket.error', exc_info=True) self.logger.error('socket.error', exc_info=True)
if autoreconnect: if autoreconnect and not self._kill:
self.connect() self.connect()
else: else:
return return

Wyświetl plik

@ -42,6 +42,7 @@ class OgnClientTest(unittest.TestCase):
client.disconnect() client.disconnect()
client.sock.shutdown.assert_called_once_with(0) client.sock.shutdown.assert_called_once_with(0)
client.sock.close.assert_called_once_with() client.sock.close.assert_called_once_with()
self.assertTrue(client._kill)
def test_50_live_messages(self): def test_50_live_messages(self):
print("Enter") print("Enter")