folly::observer::Observer

Observer - a library which lets you create objects which track updates of their dependencies and get re-computed when any of the dependencies changes.

Synopsis

Declared in <folly/observer/Observer.h>

template<typename T>
class Observer;

Description

The preferred way to read the observed value is with(), which keeps the snapshot alive for the duration of the lambda and prevents read-after-free:

Observer<Config> configObserver = ...; auto result = configObserver.with([]Config& cfg) { return cfg.getSomeValue(); });

For cases where you need to store or pass the snapshot around, you can get one explicitly:

Observer<int> myObserver = ...; Snapshot<int> mySnapshot = myObserver.getSnapshot();

or simply

Snapshot<int> mySnapshot = *myObserver;

Snapshot will hold a view of the object, even if object in the Observer gets updated.

Note: fetching a snapshot from Observer will never block/fail. And returned snapshot will never contain a nullptr.

What makes Observer powerful is its ability to track updates to other Observers. Imagine we have two separate Observers A and B which hold integers.

Observer<int> observerA = ...; Observer<int> observerB = ...;

To compute a sum of A and B we can create a new Observer which would track updates to A and B and re-compute the sum only when necessary.

Observer<int> sumObserver = makeObserver( [observerA, observerB]{ int a = **observerA; int b = **observerB; return a + b; });

int sum = **sumObserver;

Notice that a + b will be only called when either a or b is changed. Getting a snapshot from sumObserver won't trigger any re-computation.

Getting an Observer snapshot involves acquiring a shared_ptr, which can be expensive, especially if several threads do so concurrently. If the cost of getSnapshot() is noticeable, alternative Observer implementations are available, offering different trade-offs:

  • If T is a type for which std::atomic<T> is lock-free (all word-sized PODs for example), AtomicObserver and ReadMostlyAtomicObserver offer the best performance at no additional memory cost.

  • TLObserver stores a thread-local snapshot, so that it can be accessed without synchronization (except when it needs updating). This however can consume significant amounts of memory by stranding old snapshots in threads that do not access, and thus refresh, the observer.

  • HazptrObserver uses hazard pointers to protect the snapshot, which offer high read scalability and low cost, but the snapshot should be held as little as possible and should not cross coroutine suspension points.

  • ReadMostlyTLObserver returns a snapshot that can be used like a regular shared_ptr. Scalability and cost are comparable to HazptrObserver, but the snapshots can be held for arbitrary time. Memory cost is a small constant for each thread that acquires a snapshot.

  • CoreCachedObserver can be used if a std::shared_ptr<T> is strictly required. Read scalability is comparable to the previous options, but cost is moderately higher. Memory cost is a small constant for each CPU in the system.

See ObserverCreator class if you want to wrap any existing subscription API in an Observer object.

Member Functions

NameDescription
Observer [constructor]Constructs an Observer wrapping the given core.
addCallback Add a callback to be called when the Observer is updated. The callback will be removed when the returned CallbackHandle is destroyed.
getCore Returns the underlying core.
getCreatorInvokeResultTypeInfo Returns type info for the creator's invoke result.
getCreatorName Returns the creator's name.
getCreatorTypeInfo Returns type info for the creator functor.
getSnapshot Never throws or blocks. Never returns an empty snapshot. Prefer with() for short-lived access to avoid read-after-free bugs.
needRefresh needRefresh overloads
operator* Returns a snapshot of the current observed value.
with Invoke a function with the current observed value. The snapshot is held alive for the duration of the call, preventing read-after-free when accessing members of the observed object.

Friends

NameDescription
folly::observer::ObserverCreatorCreates an Observer from an observable using the given traits.

Non-Member Functions

NameDescription
makeAtomicObserverSame as makeObserver(...), but creates AtomicObserver.
makeCoreCachedObserverSame as makeObserver(...), but creates CoreCachedObserver.
makeHazptrObserverSame as makeObserver(...), but creates HazptrObserver.
makeObserverCreates an Observer from a creator returning a shared pointer.
makeObservermakeObserver(...) creates a new Observer<T> object given a functor to compute it. The functor can return T or std::shared_ptr<const T>.
makeObserverCreates an Observer from a creator returning an Observer.
makeReadMostlyAtomicObserverSame as makeObserver(...), but creates ReadMostlyAtomicObserver.
makeReadMostlyTLObserverSame as makeObserver(...), but creates ReadMostlyTLObserver.
makeStaticObserverCreates a static Observer holding a shared pointer value.
makeStaticObserverThe returned Observer will never update and always return the passed value.
makeTLObserverSame as makeObserver(...), but creates TLObserver.
makeValueObserverA more efficient short-cut for makeValueObserver(makeObserver(...)).
makeValueObserverCreates a value Observer from a creator returning a shared pointer.
makeValueObserverThe returned Observer will proxy updates from the input observer, but will skip updates that contain the same (according to operator==) value even if the actual object in the update is different.
unwrapReturns the observer unchanged.
unwrapFlattens a nested Observer into a single Observer.
unwrapValueFlattens a nested Observer into a single value Observer.
unwrapValueReturns the observer unchanged.
withJitterThe returned Observer will proxy updates from the input observer but will delay the propagation of each update by some duration between 0 ms and lag + jitter. In addition, if an update arrives while no preceding jittered updates are still in flight, then the delay applied to the latest update will be a uniformly random duration between lag - jitter and lag + jitter.
::folly::settings::getObserverGets a folly::observer::Observer<T> for a given setting. For example: folly::settings::getObserver(FOLLY_SETTING(project, retention))