From 79918fef34a65afcea2eb30e370b036fd550911d Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Sun, 17 Nov 2024 05:14:52 -0700 Subject: [PATCH] fix race condition when using semantic methods Enumerable and predicate methods will now behave better when they refer to stale proxies - filtering methods will just remove them. I run into this occasionally when I have multiple rules firing, and some of them are adding or removing items - and calling items.locations would raise a NoMethodError for #location? due to a race condition. This should no longer happen - it will just ignore the now-stale items. Signed-off-by: Cody Cutrer --- lib/openhab/core/items/proxy.rb | 29 +++++++++++++++++++++++++++ spec/openhab/core/items/proxy_spec.rb | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/lib/openhab/core/items/proxy.rb b/lib/openhab/core/items/proxy.rb index 2a7a7d0f2e..84ba60a3b9 100644 --- a/lib/openhab/core/items/proxy.rb +++ b/lib/openhab/core/items/proxy.rb @@ -97,6 +97,35 @@ def members __getobj__.members end + # Several methods can just return nil when it's a dummy item + # This helps when you're doing something like `items.locations.select {}` + # when items are getting created and removed in a concurrent thread to + # not have errors because an item disappeared + %i[ + equipment + equipment? + equipment_type + location + location? + location_type + member_of? + point? + point_type + property_type + semantic? + semantic_type + tagged? + ].each do |m| + class_eval <<~RUBY, __FILE__, __LINE__ + 1 + def #{m}(*args) # def equipment(*args) + target = __getobj__ # target = __getobj__ + return nil if target.nil? # return nil if target.nil? + # + target.#{m}(*args) # target.equipment(*args) + end # end + RUBY + end + # @return [String] def to_s return name if __getobj__.nil? diff --git a/spec/openhab/core/items/proxy_spec.rb b/spec/openhab/core/items/proxy_spec.rb index 74e44f7af6..3790bb89b1 100644 --- a/spec/openhab/core/items/proxy_spec.rb +++ b/spec/openhab/core/items/proxy_spec.rb @@ -98,6 +98,10 @@ expect(item).not_to respond_to(:command) expect { item.command }.to raise_error(NoMethodError) end + + it "disappears when calling semantic predicates on an array" do + expect([item].locations).to eql [] + end end it "does not respond to GroupItem#members if it's backed by a non-GroupItem" do