lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Alex Bilyk pidió:

> Given a co-routine I would
> like to associate a user data object with it that would get collected
before
> the co-routine itself does. In other words, given a co-routine with no
> outstanding references to it I would like for some designated user data
get
> garbage collected/finalized before the co-routine itself does.

OK, I finally figured out how to do this.

You need to create a proxy for the coroutine; the proxy would implement the
coroutines methods by calling the coroutine. You use the proxy instead of
the coroutine.

The proxy does not maintain a reference to the coroutine, but it does
maintain a reference to the userdata. There is a weak-keyed table from
userdata to coroutine, and the proxy uses that to find the coroutine (it
has a reference to the userdata).

Now, the only strong reference to the userdata is in the proxy, and the
only reference to the coroutine is in a weak-keyed table, where the
coroutine reference is a strong value. The userdata does not have any
references other than the weak table itself, and the coroutine has no
reference to the userdata, so there is no circular reference in the weak
table.

When the proxy is unreachable, so is the userdata and therefore its
finalisation method will run. The coroutine is also technically unreachable
but it will have been marked because it is a strong value in a weak-keyed
table, so it will still be around. The userdata can use the weak table
(using itself as a key) to access the coroutine, if it needs to. As long as
the userdata doesn't resurrect the coroutine by storing a reference to it
in some visible object, the coroutine will disappear on the next garbage
collection cycle.

Phew!

So we have something like this: (not tested)

do
  local udata2coro = setmetatable({}, {__mode = "k"})

  -- create a coroutine (proxy) and associate it with a udata
  -- all this effort will be wasted if the udata doesn't have
  -- a __gc method.

  function cocreate(fn, udata)
    local coro = co.create(fn)
    udata2coro[udata] = coro

    -- we must not reference coro anywhere in the proxy table.
    -- But the upvalue to udata is ok (even necessary)

    local proxy = {}

    -- if you knew how many arguments your coroutines wanted,
    -- you could write this more efficiently.
    function proxy:resume(...)
      return co.resume(udata2coro[udata], unpack(arg))
    end

    function proxy:status()
      return co.status(udata2coro[udata])
    end

    return proxy
  end
end

Well, that wasn't too hard. Let me know if it works.