Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlt_timetablecache
[Package bdlt]

Provide an efficient cache for read-only bdlt::Timetable objects. More...

Namespaces

namespace  bdlt

Detailed Description

Outline
Purpose:
Provide an efficient cache for read-only bdlt::Timetable objects.
Classes:
bdlt::TimetableCache cache for read-only timetables loaded on demand
See also:
Component bdlt_timetable, Component bdlt_timetableloader
Description:
This component defines the bdlt::TimetableCache class, a cache for read-only bdlt::Timetable objects. The bdlt::TimetableCache class defines two methods for fetching timetables from the cache: a manipulator called getTimetable and an accessor called lookupTimetable. Timetables are identified by name using C-style strings, and both retrieval methods return a bsl::shared_ptr<const bdlt::Timetable>.
The first time a timetable is requested from the cache using the getTimetable manipulator, the identified timetable is loaded into the cache using the loader that was supplied upon construction of the cache (see bdlt_timetableloader); a reference to that newly-loaded timetable is then returned. Subsequent requests for the same timetable, using either the getTimetable or lookupTimetable method, are efficiently satisfied by returning references to the cached instance. The lookupTimetable accessor differs from the getTimetable manipulator in that when a request is made through the accessor for a timetable that is not present in the cache, the timetable is not loaded as a side-effect. In this case, an empty bsl::shared_ptr<const bdlt::Timetable> is returned instead, which is effectively a null pointer. Note that the timetable-naming convention in effect for a given cache is determined by the concrete loader supplied at construction of the cache.
Timetables stored in a cache can be explicitly invalidated; the invalidate method is used to invalidate a single timetable and invalidateAll invalidates all timetables in the cache. Invalidated timetables are removed from the cache. However, a timetable that has been invalidated in the cache remains valid to all outstanding references to it, obtained via earlier calls to the getTimetable and lookupTimetable methods, until all of those references have been destroyed. Note that a subsequent request, using the getTimetable manipulator, for a timetable that has been invalidated incurs the overhead of once again loading that timetable into the cache.
Timetables can also be invalidated on the basis of a timeout. To use this feature of bdlt::TimetableCache, a bsls::TimeInterval timeout must be supplied at construction. When a timeout is in effect for a cache, requests for a timetable from the cache using the getTimetable manipulator may incur the reloading of the timetable if the one in the cache has expired (i.e., the time interval defined by the timeout value has elapsed since the timetable was last loaded). In the case of the lookupTimetable accessor, an empty bsl::shared_ptr<const bdlt::Timetable> is returned if the requested timetable is found to have expired.
Thread Safety:
The bdlt::TimetableCache class is fully thread-safe (see bsldoc_glossary) provided that the allocator supplied at construction and the default allocator in effect during the lifetime of cache objects are both fully thread-safe.
Usage:
The following example illustrates how to use a bdlt::TimetableCache.
Example 1: Using a bdlt::TimetableCache:
This example shows basic use of a bdlt::TimetableCache object.
In this example, we assume a hypothetical timetable loader, MyTimetableLoader, the details of which are not important other than that it supports timetables identified by "ZERO", "ONE", and "TWO". Furthermore, the value of the initial transition code for each of these timetables is given by the timetable's name (e.g., if Z has the value of the timetable identified as "ZERO", then 0 == Z.initialTransitionCode()).
First, we create a timetable loader, an instance of MyTimetableLoader, and use it, in turn, to create a cache. For the purposes of this example, it is sufficient to let the cache use the default allocator:
  MyTimetableLoader    loader;
  bdlt::TimetableCache cache(&loader);
Next, we retrieve the timetable twoA, identified by "TWO", verify that the loading of that timetable into the cache was successful (twoA.get() is non-null), and verify that 2 is the value of the initial transition code for timetable "TWO":
  bsl::shared_ptr<const bdlt::Timetable> twoA = cache.getTimetable("TWO");

  assert(twoA.get());
  assert(2 == twoA->initialTransitionCode());
Then, we fetch the timetable identified by "ONE", this time verifying that 1 is the value of the initial transition code for the "ONE" timetable:
  bsl::shared_ptr<const bdlt::Timetable> oneA = cache.getTimetable("ONE");

  assert(oneA.get());
  assert(1 == oneA->initialTransitionCode());
Next, we retrieve the "ONE" timetable again, this time via the lookupTimetable accessor, and note that the request is satisfied by the timetable that is already in the cache:
  const bdlt::TimetableCache& readonlyCache = cache;

  bsl::shared_ptr<const bdlt::Timetable> oneB =
                                        readonlyCache.lookupTimetable("ONE");

  assert(oneA.get() == oneB.get());
Then, we invalidate the "TWO" timetable in the cache and immediately fetch it again. The call to invalidate removed the "TWO" timetable from the cache, so it had to be reloaded into the cache to satisfy the request:
  int numInvalidated = cache.invalidate("TWO");
  assert(1 == numInvalidated);

  bsl::shared_ptr<const bdlt::Timetable> twoB = cache.getTimetable("TWO");

  assert(twoB.get() != twoA.get());
  assert(twoB.get());
  assert(2 == twoB->initialTransitionCode());
Next, all timetables in the cache are invalidated, then reloaded:
  numInvalidated = cache.invalidateAll();
  assert(2 == numInvalidated);

  bsl::shared_ptr<const bdlt::Timetable> twoC = cache.getTimetable("TWO");

  assert(twoC.get() != twoA.get());
  assert(twoC.get() != twoB.get());
  assert(twoC.get());
  assert(2 == twoC->initialTransitionCode());

  bsl::shared_ptr<const bdlt::Timetable> oneC = cache.getTimetable("ONE");

  assert(oneC.get() != oneA.get());
  assert(oneC.get() != oneB.get());
  assert(oneC.get());
  assert(1 == oneC->initialTransitionCode());
Now, verify that references to timetables that were invalidated in the cache are still valid for clients that obtained references to them before they were made invalid:
  assert(1 == oneA->initialTransitionCode());
  assert(1 == oneB->initialTransitionCode());

  assert(2 == twoA->initialTransitionCode());
  assert(2 == twoB->initialTransitionCode());
When twoA, twoB, oneA, and oneB go out of scope, the resources used by the timetables to which they refer are automatically reclaimed.
Finally, using the lookupTimetable accessor, we attempt to retrieve a timetable that has not yet been loaded into the cache, but that we know to be supported by the timetable loader. Since the lookupTimetable accessor does not load timetables into the cache as a side-effect, the request fails:
  bsl::shared_ptr<const bdlt::Timetable> zero =
                                       readonlyCache.lookupTimetable("ZERO");

  assert(!zero.get());
Example 2: A Timetable Cache with a Timeout:
This second example shows the effects on a bdlt::TimetableCache object that is constructed to have a timeout value. Note that the following snippets of code assume a platform-independent sleepSeconds method that sleeps for the specified number of seconds.
First, we create a timetable loader and a timetable cache. The cache is constructed to have a timeout of 3 seconds. Of course, such a short timeout is inappropriate for production use, but it is necessary for illustrating the effects of a timeout in this example. As in example 1 (above), we again let the cache use the default allocator:
  MyTimetableLoader           loader;
  bdlt::TimetableCache        cache(&loader, bsls::TimeInterval(3, 0));
  const bdlt::TimetableCache& readonlyCache = cache;
Next, we retrieve the timetable identified by "ZERO" from the cache:
  bsl::shared_ptr<const bdlt::Timetable> zeroA = cache.getTimetable("ZERO");

  assert(zeroA.get());
Next, we sleep for 2 seconds before retrieving the "ONE" timetable:
  sleepSeconds(2);

  bsl::shared_ptr<const bdlt::Timetable> oneA = cache.getTimetable("ONE");

  assert(oneA.get());
Next, we sleep for 2 more seconds before attempting to retrieve the "ZERO" timetable again, this time using the lookupTimetable accessor. Since the cumulative sleep time exceeds the timeout value established for the cache when it was constructed, the "ZERO" timetable has expired; hence, it has been removed from the cache:
  sleepSeconds(2);

  bsl::shared_ptr<const bdlt::Timetable> zeroB =
                                       readonlyCache.lookupTimetable("ZERO");

  assert(!zeroB.get());
Next, we verify that the "ONE" timetable is still available in the cache:
  bsl::shared_ptr<const bdlt::Timetable> oneB =
                                        readonlyCache.lookupTimetable("ONE");

  assert(oneA.get() == oneB.get());
Finally, we sleep for an additional 2 seconds and verify that the "ONE" timetable has also expired:
  sleepSeconds(2);

  bsl::shared_ptr<const bdlt::Timetable> oneC =
                                        readonlyCache.lookupTimetable("ONE");

  assert(!oneC.get());