[tuya] Avoid leaking polling jobs (#21040)

Signed-off-by: Mike Jagdis <mjagdis@eris-associates.co.uk>
This commit is contained in:
mjagdis
2026-06-24 23:12:54 +02:00
committed by GitHub
parent 1bec51d1ab
commit f9a1b46876
@@ -170,6 +170,7 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
// re-measure on a function change.
TuyaDevice tuyaDevice = this.tuyaDevice;
if (tuyaDevice != null) {
synchronized (this) {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
pollingJob.cancel(true);
@@ -178,6 +179,7 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
pollBurst = 3;
this.pollingJob = scheduler.scheduleWithFixedDelay(this::burstPoller, 0, 1, TimeUnit.SECONDS);
}
}
} else if (!missingStatus) {
// If we have updates for everything we can stand down the burst polling.
pollBurst = 0;
@@ -193,6 +195,8 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
tuyaDevice.refreshStatus();
pollBurst = pollBurst - 1;
} else {
synchronized (this) {
if (!Thread.interrupted()) {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
this.pollingJob = null;
@@ -208,6 +212,8 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
}
}
}
}
}
private void processChannelStatus(Integer dp, Object value) {
String channelId = dpToChannelId.get(dp);
@@ -325,29 +331,33 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
TuyaDevice tuyaDevice = this.tuyaDevice;
if (tuyaDevice != null) {
synchronized (this) {
if (pollingJob == null) {
// When we first connect the device state is unknown so we want to query for everything.
// Some devices seem to initialize their stacks in the wrong order and
// requests that come too soon can be either ignored completely or responded
// to with a, "not supported". The lower level protocol handler suggests an
// initial delay where it might be advisable.
this.pollingJob = scheduler.schedule(() -> {
pollingJob = scheduler.schedule(() -> {
tuyaDevice.requestStatus();
// After that we poll for the measurable DPs.
int pollingInterval = configuration.pollingInterval;
if (pollingInterval > 0) {
synchronized (this) {
// The first refresh request is immediate because we do not know how old
// the current status might be.
pollingJob = scheduler.scheduleWithFixedDelay(() -> {
tuyaDevice.refreshStatus();
}, 0, pollingInterval, TimeUnit.SECONDS);
}
}
}, initialDelay, TimeUnit.MILLISECONDS);
} else {
logger.debug("{}: polling job already exists?!?", thing.getUID().getId());
}
}
}
// start learning code if thing is online and presents 'ir-code' channel
channelIdToChannelTypeUID.entrySet().stream().filter(e -> CHANNEL_TYPE_UID_IR_CODE.equals(e.getValue()))
@@ -358,10 +368,12 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "@text/online.wait-for-device");
synchronized (this) {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
pollingJob.cancel(true);
this.pollingJob = null;
pollingJob.cancel(true);
}
}
if (channelIdToChannelTypeUID.containsValue(CHANNEL_TYPE_UID_IR_CODE)) {
@@ -534,10 +546,12 @@ public class TuyaDeviceHandler extends BaseThingHandler implements DeviceInfoSub
public void dispose() {
logger.debug("{}: dispose", thing.getUID().getId());
ScheduledFuture<?> future = this.pollingJob;
if (future != null) {
synchronized (this) {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
this.pollingJob = null;
future.cancel(true);
pollingJob.cancel(true);
}
}
udpDiscoveryListener.unregisterListener(this);