AVar safety in a debouncer

I’m experimenting with a debouncer interface (full snippet available here):

newtype Debouncer :: Type -> Type
newtype Debouncer a = Debouncer
  { ref :: AVar (Aff Unit)
  , delayMillis :: Milliseconds
  }

type Task :: Type -> Type
type Task a =
  { action :: Aff a
  , callback :: a -> Effect Unit
  }

new :: forall a. Milliseconds -> Effect (Debouncer a)
new delayMillis = do
  ref <- AVar.new $ pure unit
  pure $ Debouncer { ref, delayMillis }

run :: forall a. Debouncer a -> Task a -> Effect Unit
run (Debouncer { ref, delayMillis }) { action, callback } = do
  Aff.runAff_ (handler callback) do
    -- 1. Kill the previous task instance if it's still queued.
    Bind.join $ AffAVar.take ref
    -- 2. Fork the current task instance.
    fiber <- Aff.forkAff do
      Aff.delay delayMillis
      action
    -- 3. Store an effect that, when called, will kill the current task instance.
    AffAVar.put (Aff.killFiber killedByDebouncer fiber) ref
    -- 4. Wait until the task completes or was killed.
    Aff.joinFiber fiber

This seems to be working well from initial testing, but I’m curious if it could use more safety. Specifically, is it possible for the outer Aff within run to be somehow killed externally after the AVar has been taken from (1) but before the new kill action can be put into the AVar (3)?

I want to avoid the possibility of the AVar being taken from but then never refilled, causing future calls to run to block indefinitely. I see that a bracket operation does exist in purescript-aff, but I’m not sure if it’s necessary/warranted for this use case where the Aff computations in run aren’t exposed (minus the user-specified action).

This code is targeting the browser and the specific use case is hitting a backend on form input changes.