vpp_papi: reduce memory leaks 22/10622/3
authorKlement Sekera <ksekera@cisco.com>
Sat, 17 Feb 2018 09:58:37 +0000 (10:58 +0100)
committerDamjan Marion <dmarion.lists@gmail.com>
Sat, 17 Feb 2018 20:42:49 +0000 (20:42 +0000)
This changes makes unused VPP objects collectable by garbage collector,
allowing running all `make test` tests again instead of python crashing
due to running out of memory.

Change-Id: I0e271c2b3f195d9d3b64840f9f11144da0fe967d
Signed-off-by: Klement Sekera <ksekera@cisco.com>
src/vpp-api/python/vpp_papi.py

index 1d92a41..25a8369 100644 (file)
@@ -23,6 +23,7 @@ import struct
 import json
 import threading
 import fnmatch
+import weakref
 import atexit
 from cffi import FFI
 import cffi
@@ -55,11 +56,12 @@ void vac_set_error_handler(vac_error_callback_t);
 # Barfs on failure, no need to check success.
 vpp_api = ffi.dlopen('libvppapiclient.so')
 
-def vpp_atexit(self):
+def vpp_atexit(vpp_weakref):
     """Clean up VPP connection on shutdown."""
-    if self.connected:
-        self.logger.debug('Cleaning up VPP on exit')
-        self.disconnect()
+    vpp_instance = vpp_weakref()
+    if vpp_instance.connected:
+        vpp_instance.logger.debug('Cleaning up VPP on exit')
+        vpp_instance.disconnect()
 
 vpp_object = None
 
@@ -136,11 +138,7 @@ class VPP():
         self.message_queue = queue.Queue()
         self.read_timeout = read_timeout
         self.vpp_api = vpp_api
-        if async_thread:
-            self.event_thread = threading.Thread(
-                target=self.thread_msg_handler)
-            self.event_thread.daemon = True
-            self.event_thread.start()
+        self.async_thread = async_thread
 
         if not apifiles:
             # Pick up API definitions from default directory
@@ -168,7 +166,7 @@ class VPP():
             raise ValueError(1, 'Missing JSON message definitions')
 
         # Make sure we allow VPP to clean up the message rings.
-        atexit.register(vpp_atexit, self)
+        atexit.register(vpp_atexit, weakref.ref(self))
 
         # Register error handler
         vpp_api.vac_set_error_handler(vac_error_handler)
@@ -664,6 +662,11 @@ class VPP():
         self.control_ping_index = vpp_api.vac_get_msg_index(
             ('control_ping' + '_' + crc[2:]).encode())
         self.control_ping_msgdef = self.messages['control_ping']
+        if self.async_thread:
+            self.event_thread = threading.Thread(
+                target=self.thread_msg_handler)
+            self.event_thread.daemon = True
+            self.event_thread.start()
         return rv
 
     def connect(self, name, chroot_prefix=None, async=False, rx_qlen=32):
@@ -695,6 +698,7 @@ class VPP():
         """Detach from VPP."""
         rv = vpp_api.vac_disconnect()
         self.connected = False
+        self.message_queue.put("terminate event thread")
         return rv
 
     def msg_handler_sync(self, msg):
@@ -712,8 +716,6 @@ class VPP():
         if hasattr(r, 'context') and r.context > 0:
             context = r.context
 
-        msgname = type(r).__name__
-
         if context == 0:
             # No context -> async notification that we feed to the callback
             self.message_queue.put_nowait(r)
@@ -863,6 +865,8 @@ class VPP():
         """
         while True:
             r = self.message_queue.get()
+            if r == "terminate event thread":
+                break
             msgname = type(r).__name__
             if self.event_callback:
                 self.event_callback(msgname, r)