Try_create_device: though device may be "added", not always "init"ed

Hi, @rossetyler. Sorry for the delay, the engineering team mentioned the following about your sample:

  • In the driver you aren’t using any sockets at all, so there are no calls to receive but you use both cosock.socket.sleep and cosock.socket.select
  • In this file it is not clear if one of those functions is getting called in the added lifecycle and that is blocking init.
  • You should check if the creation of the devices was successful using assert(driver.try_create_device(...)) or local success, err = driver.try_create_device(...). This way, you can see if there were immediate errors.
    • Especially because, in your sample, you’re calling try_create_device in a hot loop 100 times and this might be reaching the rate limit of devices created in a given timespan.

In this case, this number of devices was an example, or do you actually have drivers that create 100 devices? If so, could you share the details of that use case?

  • Any time something could block the current thread, you can avoid that entirely by calling cosock.spawn. So, something like cosock.spawn(function() local value, err = socket:receive() end) would allow the use of the device thread for any other device events. For example:

This sample shows how the device thread can be blocked


-- This driver will lock up any device thread by
-- yielding the thread into a deadlock because
-- init cannot be processed until added is complete
-- but added is relying on init to complete

local tx, rx = cosock.channel.new()
local function added(driver, device)
  -- wait for init
  local init, err = rx:receive()
end

local function init(driver, device)
  -- send init
  tx:send(device.id)
end

local driver = Driver("...", {
  lifecycle_handlers = {
    added = added,
    init = init,
  }
})

Considering the previous implementation, this is how we can avoid the blockage using cosock.spawn

local tx, rx = cosock.channel.new()
local function added(driver, device)
  cosock.spawn(function()
    local init, err = rx:receive()
    
  end)
  -- wait for init
end

local function init(driver, device)
  -- send init
  tx:send(device.id)
end

local driver = Driver("...", {
  lifecycle_handlers = {
    added = added,
    init = init,
  }
})

cc @blueyetisoftware