Skip to content

Commit 5be1c72

Browse files
committed
fix: Add timeouts to token receive handling for existing devices.
This is to avoid a situation I've noticed in testing, where the receive handle can become hung indefinitely.
1 parent 2c3997e commit 5be1c72

File tree

1 file changed

+70
-2
lines changed

1 file changed

+70
-2
lines changed

drivers/SmartThings/sonos/src/lifecycle_handlers.lua

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,76 @@ function SonosDriverLifecycleHandlers.initialize_device(driver, device)
7777
log.error("token event bus closed, aborting initialization")
7878
return
7979
end
80-
driver:request_oauth_token()
81-
token_event_receive:receive()
80+
token_event_receive:settimeout(30)
81+
local token, token_recv_err
82+
-- max 30 mins
83+
local backoff_builder = utils.backoff_builder(60 * 30, 30, 2)
84+
if not driver:is_waiting_for_oauth_token() then
85+
local _, request_token_err = driver:request_oauth_token()
86+
if request_token_err then
87+
log.warn(string.format("Error sending token request: %s", request_token_err))
88+
end
89+
end
90+
91+
local backoff_timer = nil
92+
while not token do
93+
local send_request = false
94+
-- we use the backoff to create a timer and utilize a select loop here, instead of
95+
-- utilizing a sleep, so that we can create a long delay on our polling of the cloud
96+
-- without putting ourselves in a situation where we're sleeping for an extended period
97+
-- of time so that we don't sleep through the users's log-in attempt and fail to resume
98+
-- our connection attempts in a timely manner.
99+
--
100+
-- The backoff caps at 30 mins, as commented above
101+
if not backoff_timer then
102+
backoff_timer = cosock.timer.create_oneshot(backoff_builder())
103+
end
104+
local token_recv_ready, _, token_select_err =
105+
cosock.socket.select({ token_event_receive, backoff_timer }, nil, nil)
106+
107+
if token_select_err then
108+
log.warn(string.format("select error: %s", token_select_err))
109+
end
110+
111+
token, token_recv_err = nil, nil
112+
for _, receiver in pairs(token_recv_ready or {}) do
113+
if receiver == backoff_timer then
114+
-- we just make a note that the backoff has elapsed, rather than
115+
-- put a request in flight immediately.
116+
--
117+
-- This is just in case both receivers are ready, so that we can prioritize
118+
-- handling the token instead of putting another request in flight.
119+
send_request = true
120+
backoff_timer = nil
121+
end
122+
123+
if receiver == token_event_receive then
124+
token, token_recv_err = token_event_receive:receive()
125+
end
126+
end
127+
128+
if token_recv_err == "timeout" then
129+
log.debug("timeout waiting for OAuth token in reconnect task")
130+
elseif token_recv_err and not token then
131+
log.warn(
132+
string.format(
133+
"Unexpected error on token event receive bus: %s",
134+
token_recv_err
135+
)
136+
)
137+
end
138+
139+
if send_request then
140+
if not driver:is_waiting_for_oauth_token() then
141+
local _, request_token_err = driver:request_oauth_token()
142+
if request_token_err then
143+
log.warn(
144+
string.format("Error sending token request: %s", request_token_err)
145+
)
146+
end
147+
end
148+
end
149+
end
82150
else
83151
device.log.error(
84152
string.format(

0 commit comments

Comments
 (0)