The Thread-Safe Interface design pattern minimizes locking overhead and ensures that
intra-component method calls do not incur 'self-deadlock' by trying to reacquire a lock that is
held by the component already.
Implementation
The Thread-Safe Interface pattern can be implemented using two activities:
1. Determine the interface and corresponding implementation methods. The interface
methods define the public API of the component. For each interface method, define a
corresponding implementation method.
The interface and implementation methods for File_Cache can be defined as
follows:
template <class LOCK>
class File_Cache
{
public:
// The following two interface methods just
// acquire/release the <LOCK> and forward to
// their corresponding implementation methods.
const void *lookup (const string &path) const;
void insert (const string &path);
private:
// The following two implementation methods do not
// acquire/release the <LOCK> and perform the actual
// work associated with managing the <File_Cache>.
const void *lookup_i (const string &path);
const void insert_i (const string &path);
// ... Other implementation methods omitted
};
Program the interface and implementation methods. The bodies of the interface and
implementation methods are programmed according to the design conventions
described in the Solution section.
Our File_Cache implementation applies Thread-Safe Interface to minimize
locking overhead and prevent self-deadlock in class methods:
template <class LOCK>
class File_Cache
{
public:
// Return a pointer to the memory-mapped file
// associated with <path> name, adding it to
// the cache if it doesn't exist.
const void *lookup (const string &path) const
{
// Use the Scoped Locking idiom to acquire
// and release the <lock_> automatically.
Guard<LOCK> guard (lock_);
return lookup_i (path);
}
// Add <path> name to the file cache.
void insert (const string &path) {
// Use the Scoped Locking idiom to acquire
// and release the <lock_> automatically.
Guard<LOCK> guard (lock_);
insert_i (path);
}
private:
mutable LOCK lock_; // The strategized locking object
// The following implementation methods do not
// acquire or release <lock_> and perform their
// work without calling any interface methods.
const void *lookup_i (const string &path) const {
const void *file_pointer = check_cache_i(path);
if (file_pointer == 0)
{
// If <path> name isn't in the cache then
// insert it and look it up again.
insert_i (path);
file_pointer = check_cache_i (path);
// The calls to implementation methods
// <insert_i> and <check_cache_i>assume
// that the lock is held and performwork.
}
return file_pointer;
}
const void *check_cache_i (const string &)
{ /* */ }
const void insert_i (const string &) { /* ... */ }
// ... other private methods and data omitted
};