gattlib-py: Fix some memory leaks

pull/185/merge 0.4.8
Olivier Martin 2024-03-13 13:49:13 +01:00
parent d2fb01d85e
commit b2c4094cb6
5 changed files with 54 additions and 8 deletions

View File

@ -191,3 +191,10 @@ void gattlib_handler_dispatch_to_thread(struct gattlib_handler* handler, void (*
return;
}
}
// Helper function to free memory from Python frontend
void gattlib_free_mem(void *ptr) {
if (ptr != NULL) {
free(ptr);
}
}

View File

@ -28,10 +28,10 @@ struct on_eddystone_discovered_device_arg {
static void on_eddystone_discovered_device(void *adapter, const char* addr, const char* name, void *user_data)
{
struct on_eddystone_discovered_device_arg *callback_data = user_data;
gattlib_advertisement_data_t *advertisement_data;
gattlib_advertisement_data_t *advertisement_data = NULL;
size_t advertisement_data_count;
uint16_t manufacturer_id;
uint8_t *manufacturer_data;
uint8_t *manufacturer_data = NULL;
size_t manufacturer_data_size;
int ret;
@ -46,6 +46,13 @@ static void on_eddystone_discovered_device(void *adapter, const char* addr, cons
advertisement_data, advertisement_data_count,
manufacturer_id, manufacturer_data, manufacturer_data_size,
callback_data->user_data);
if (advertisement_data != NULL) {
free(advertisement_data);
}
if (manufacturer_data != NULL) {
free(manufacturer_data);
}
}
int gattlib_adapter_scan_eddystone(void *adapter, int16_t rssi_threshold, uint32_t eddystone_types,

View File

@ -217,3 +217,7 @@ gattlib_get_advertisement_data_from_mac.argtypes = [c_void_p, c_char_p, POINTER(
# int gattlib_mainloop_python(PyObject *handler, PyObject *user_data)
gattlib_mainloop = gattlib.gattlib_mainloop_python
gattlib_mainloop.argtypes = [py_object, py_object]
# void gattlib_free_mem(void *ptr])
gattlib_free_mem = gattlib.gattlib_free_mem
gattlib_free_mem.argtypes = [c_void_p]

View File

@ -268,4 +268,7 @@ class Adapter:
for i in range(_manufacturer_data_len.value):
manufacturer_data[i] = c_bytearray.contents[i] & 0xFF
gattlib_free_mem(_advertisement_data)
gattlib_free_mem(_manufacturer_data)
return advertisement_data, _manufacturer_id.value, manufacturer_data

View File

@ -40,7 +40,10 @@ class Device:
self._addr = addr
self._name = name
self._connection = None
# We use a lock because on disconnection, we will set self._connection to None
self._connection_lock = threading.Lock()
# We use a lock on disconnection to ensure the memory is safely freed
self._disconnection_lock = threading.Lock()
self.on_connection_callback = None
self.on_connection_error_callback = None
@ -51,6 +54,10 @@ class Device:
# Dictionnary for GATT characteristic callback
self._gatt_characteristic_callbacks = {}
# Memory that could be allocated by native gattlib
self._services_ptr = None
self._characteristics_ptr = None
@property
def mac_address(self) -> str:
"""Return Device MAC Address"""
@ -109,9 +116,24 @@ class Device:
self.disconnection_callback = callback
def on_disconnection(user_data):
self._disconnection_lock.acquire()
if self.disconnection_callback:
self.disconnection_callback()
# On disconnection, we do not need the list of GATT services and GATT characteristics
if self._services_ptr:
gattlib_free_mem(self._services_ptr)
self._services_ptr = None
if self._characteristics_ptr:
gattlib_free_mem(self._characteristics_ptr)
self._characteristics_ptr = None
# Reset the connection handler
self._connection = None
self._disconnection_lock.release()
gattlib_register_on_disconnect(self.connection,
gattlib_disconnected_device_python_callback,
gattlib_python_callback_args(on_disconnection, user_data))
@ -130,14 +152,14 @@ class Device:
#
# Discover GATT Services
#
_services = POINTER(GattlibPrimaryService)()
self._services_ptr = POINTER(GattlibPrimaryService)()
_services_count = c_int(0)
ret = gattlib_discover_primary(self.connection, byref(_services), byref(_services_count))
ret = gattlib_discover_primary(self.connection, byref(self._services_ptr), byref(_services_count))
handle_return(ret)
self._services = {}
for i in range(0, _services_count.value):
service = GattService(self, _services[i])
service = GattService(self, self._services_ptr[i])
self._services[service.short_uuid] = service
logger.debug("Service UUID:0x%x" % service.short_uuid)
@ -145,14 +167,14 @@ class Device:
#
# Discover GATT Characteristics
#
_characteristics = POINTER(GattlibCharacteristic)()
self._characteristics_ptr = POINTER(GattlibCharacteristic)()
_characteristics_count = c_int(0)
ret = gattlib_discover_char(self.connection, byref(_characteristics), byref(_characteristics_count))
ret = gattlib_discover_char(self.connection, byref(self._characteristics_ptr), byref(_characteristics_count))
handle_return(ret)
self._characteristics = {}
for i in range(0, _characteristics_count.value):
characteristic = GattCharacteristic(self, _characteristics[i])
characteristic = GattCharacteristic(self, self._characteristics_ptr[i])
self._characteristics[characteristic.short_uuid] = characteristic
logger.debug("Characteristic UUID:0x%x" % characteristic.short_uuid)
@ -201,6 +223,9 @@ class Device:
for i in range(_manufacturer_data_len.value):
manufacturer_data[i] = c_bytearray.contents[i] & 0xFF
gattlib_free_mem(_advertisement_data)
gattlib_free_mem(_manufacturer_data)
return advertisement_data, _manufacturer_id.value, manufacturer_data
@property