Skip to content

NmrDeregisterClient can hang if it races with NmrRegisterProvider. #318

Description

@shankarseal

begin_unbind() assumes that any binding not yet in Ready/Ready is already being torn down, and returns as if unbind is pending. That is too coarse. If NmrRegisterProvider() is in the middle of binding a provider to an existing client, the new binding can already be inserted in the bindings map with status Start, but the attach callback has not yet completed. If NmrDeregisterClient() runs in that window, it can find that Start binding, skip the real unbind work, and return as though teardown is already underway. The provider registration path then finishes, flips the binding to Ready/Ready, and leaves it stranded against a client that is already marked deregistering, so the binding count never drops to zero and client deregistration can hang or assert in remove().

Thread A: provider register                        Thread B: client deregister
---------------------------                        ---------------------------
NmrRegisterProvider(P)
  perform_bind(P, C)
    bind(C, P)
      binding_count++ on C and P
      create B_new with status Start/Start
      insert B_new into bindings
    unlock
                                                   NmrDeregisterClient(C)
                                                     deactivate(C)
                                                       C.deregistering = true
                                                     perform_unbind(C)
                                                       sees B_new in bindings
                                                       begin_unbind(B_new)
                                                         status != Ready
                                                         returns "pending"
                                                         but does not start unbind
    action()   // deferred attach callback
      ClientAttachProvider(...)
      ProviderAttachClient(...)
      B_new -> Ready/Ready
                                                   wait_for_deregister_client(C)
                                                     remove(C)
                                                       binding_count still 1
                                                       waits forever / asserts

Metadata

Metadata

Assignees

Labels

Type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions