[snmp] Fix memory leak in SNMP (#8672)

* Fix memory leak in SNMP. When receiving an async request it is placed in a pending request queue. Not canceling it leads to infinty life cycle for those requests, filling up memory.
* Test the canceling of the snmp aync request by its handler.

Signed-off-by: Falk Bauer <falk.bauer@k-is.com>
This commit is contained in:
fab-kis 2020-10-10 19:11:24 +02:00 committed by Jan N. Klug
parent dce02240ce
commit 3573f89907
2 changed files with 34 additions and 0 deletions

View File

@ -52,6 +52,7 @@ import org.snmp4j.CommandResponderEvent;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.PDUv1;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.SnmpConstants;
@ -175,6 +176,15 @@ public class SnmpTargetHandler extends BaseThingHandler implements ResponseListe
if (event == null) {
return;
}
if (event.getSource() instanceof Snmp) {
// Always cancel async request when response has been received
// otherwise a memory leak is created! Not canceling a request
// immediately can be useful when sending a request to a broadcast
// address (Comment is taken from the snmp4j API doc).
((Snmp) event.getSource()).cancel(event.getRequest(), this);
}
PDU response = event.getResponse();
if (response == null) {
Exception e = event.getError();

View File

@ -24,6 +24,7 @@ import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.junit.Test;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.smi.Counter64;
import org.snmp4j.smi.Integer32;
@ -104,4 +105,27 @@ public class SnmpTargetHandlerTest extends AbstractSnmpTargetHandlerTest {
thingHandler.onResponse(event);
verify(thingHandlerCallback, atLeast(1)).stateUpdated(eq(CHANNEL_UID), eq(new DecimalType("12.4")));
}
@Test
public void testCancelingAsyncRequest() {
setup(SnmpBindingConstants.CHANNEL_TYPE_UID_NUMBER, SnmpChannelMode.READ, SnmpDatatype.FLOAT);
PDU responsePDU = new PDU(PDU.RESPONSE,
Collections.singletonList(new VariableBinding(new OID(TEST_OID), new OctetString("12.4"))));
SnmpMock source = new SnmpMock();
ResponseEvent event = new ResponseEvent(source, null, null, responsePDU, null);
thingHandler.onResponse(event);
assertEquals(1, source.cancelCallCounter);
}
class SnmpMock extends Snmp {
public int cancelCallCounter = 0;
@Override
public void cancel(PDU request, org.snmp4j.event.ResponseListener listener) {
++cancelCallCounter;
}
}
}