Principle:Tensorflow Serving Target Interface
| Knowledge Sources | |
|---|---|
| Domains | Model Serving, Core Framework |
| Last Updated | 2026-02-13 00:00 GMT |
Overview
The Target Interface principle defines a safe, lifecycle-aware mechanism for modules to receive aspired-version notifications from Sources, with guaranteed callback validity beyond object lifetime.
Description
In TensorFlow Serving, Sources produce aspired-version notifications that must be delivered to downstream modules (Managers, SourceAdapters, SourceRouters). The Target interface formalizes this receiving end of the connection.
The key challenge is lifecycle management: a Source may hold a callback to a Target that has been destroyed. The Target interface solves this with a detachment protocol:
TargetBasecreates a shared mutex and a sharedNotificationflag during construction.- The aspired-versions callback (an Observer) captures these shared objects, making it independent of the Target's lifetime.
- When the Target is destroyed, its destructor calls
Detach(), which sets the notification flag. Any subsequent callback invocations check this flag and become no-ops. - The mutex ensures no
SetAspiredVersions()call is in flight whenDetach()completes.
This design means the callback is "valid forever" from the Source's perspective -- it never becomes a dangling pointer, it simply stops doing anything after the Target is gone.
The ConnectSourceToTarget() free function encapsulates the wiring: it obtains the callback from the Target and passes it to the Source.
Usage
Apply this principle whenever implementing a module that receives aspired-version notifications. Always extend TargetBase rather than implementing Target directly. Always call Detach() at the top of every leaf class destructor. Use ConnectSourceToTarget() to wire Sources to Targets.
Theoretical Basis
The Target interface implements a safe callback pattern using shared ownership and cooperative cancellation:
Construction:
shared_mu = make_shared<mutex>()
shared_detached = make_shared<Notification>()
callback = lambda [shared_mu, shared_detached, this] (name, versions):
lock(shared_mu)
if shared_detached.notified: return // no-op
this->SetAspiredVersions(name, versions)
GetAspiredVersionsCallback:
lock(shared_mu)
if shared_detached.notified: return no_op_lambda
return callback.notifier()
Detach (called in leaf destructor):
lock(shared_mu)
delete observer // waits for in-flight calls
shared_detached.notify()
// After this point, no SetAspiredVersions can run
Key design properties:
- Callback outlives Target: Shared pointers to the mutex and notification keep the callback functional (as a no-op) after the Target is destroyed.
- No dangling references: The lambda captures shared_ptrs, not raw pointers, to synchronization primitives.
- Drain guarantee: Detach() ensures no
SetAspiredVersions()is in-flight before destruction proceeds. - Single Detach requirement: DCHECK enforces that Detach() is called exactly once, catching misuse at development time.