BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlcc_objectcatalog

Functions

 bdlcc::ObjectCatalogIter< TYPE >::operator const void * () const
 Return non-zero if the iterator is valid, and 0 otherwise.
 

Detailed Description

Outline

Purpose

Provide an efficient indexed, thread-safe object container.

Classes

See also

Description

This component provides a thread-safe and efficient templatized catalog of objects. A bdlcc::ObjectCatalog supports efficient insertion of objects through the add method, which returns a handle that can be used for further reference to the newly added element. An element can be accessed by providing its handle to the find function. Thread-safe design implies that the element is returned by value into an object buffer rather than by reference (see this package documentation for a discussion of thread-safe container design). Likewise, an element can be modified by providing its handle and a new value to the replace method. Finally, an element can be removed by passing its handle to the remove method; the handle is then no longer valid and subsequent calls to find or remove with this handle will return 0.

bdlcc::ObjectCatalogIter provides thread safe iteration through all the objects of an object catalog of parameterized TYPE. The order of the iteration is implementation defined. Thread safe iteration is provided by (read)locking the object catalog during the iterator's construction and unlocking it at the iterator's destruction. This guarantees that during the life time of an iterator, the object catalog can't be modified (however multiple threads can still concurrently read the object catalog).

Note that an object catalog has a maximum capacity of 2^23 items.

Usage

This section illustrates intended use of this component.

Example 1: Catalog Usage

Consider a client sending queries to a server asynchronously. When the response to a query arrives, the client needs to invoke the callback associated with that query. For good performance, the callback should be invoked as quickly as possible. One way to achieve this is as follows. The client creates a catalog for the functors associated with queries. It sends to the server the handle (obtained by passing the callback functor associated with the query to the add method of catalog), along with the query. The server does not interpret this handle in any way and sends it back to the client along with the computed query result. The client, upon receiving the response, gets the functor (associated with the query) back by passing the handle (contained in the response message) to the find method of catalog.

Assume the following declarations (we leave the implementations as undefined, as the definitions are largely irrelevant to this example):

/// Class simulating the query.
struct Query {
};
/// Class simulating the result of a query.
class QueryResult {
};
/// Class encapsulating the request message. It encapsulates the
/// actual query and the handle associated with the callback for the
/// query.
class RequestMsg
{
Query d_query;
int d_handle;
public:
/// Create a request message with the specified `query` and
/// `handle`.
RequestMsg(Query query, int handle)
: d_query(query)
, d_handle(handle)
{
}
/// Return the handle contained in this response message.
int handle() const
{
return d_handle;
}
};
/// Class encapsulating the response message. It encapsulates the query
/// result and the handle associated with the callback for the query.
class ResponseMsg
{
int d_handle;
public:
/// Set the "handle" contained in this response message to the
/// specified `handle`.
void setHandle(int handle)
{
d_handle = handle;
}
/// Return the query result contained in this response message.
QueryResult queryResult() const
{
return QueryResult();
}
/// Return the handle contained in this response message.
int handle() const
{
return d_handle;
}
};
/// Send the specified `msg` to the specified `peer`.
void sendMessage(RequestMsg msg, RemoteAddress peer)
{
serverMutex.lock();
peer->push(msg.handle());
serverNotEmptyCondition.signal();
serverMutex.unlock();
}
/// Get the response from the specified `peer` into the specified `msg`.
void recvMessage(ResponseMsg *msg, RemoteAddress peer)
{
serverMutex.lock();
while (peer->empty()) {
serverNotEmptyCondition.wait(&serverMutex);
}
msg->setHandle(peer->front());
peer->pop();
serverMutex.unlock();
}
/// Set the specified `query` and `callBack` to the next `Query` and its
/// associated functor (the functor to be called when the response to
/// this `Query` comes in).
void getQueryAndCallback(Query *query,
bsl::function<void(QueryResult)> *callBack)
{
(void)query;
*callBack = &queryCallBack;
}
Forward declaration.
Definition bslstl_function.h:934

Furthermore, let also the following variables be declared:

RemoteAddress serverAddress; // address of remote server
/// Catalog of query callbacks, used by the client internally to keep
/// track of callback functions across multiple queries. The invariant
/// is that each element corresponds to a pending query (i.e., the
/// callback function has not yet been or is in the process of being
/// invoked).
bdlcc::ObjectCatalog<bsl::function<void(QueryResult)> > catalog;
Definition bdlcc_objectcatalog.h:412

Now we define functions that will be used in the thread entry functions:

void testClientProcessQueryCpp()
{
int queriesToBeProcessed = NUM_QUERIES_TO_PROCESS;
while (queriesToBeProcessed--) {
Query query;
bsl::function<void(QueryResult)> callBack;
// The following call blocks until a query becomes available.
getQueryAndCallback(&query, &callBack);
// Register `callBack` in the object catalog.
int handle = catalog.add(callBack);
assert(handle);
// Send query to server in the form of a `RequestMsg`.
RequestMsg msg(query, handle);
sendMessage(msg, serverAddress);
}
}
void testClientProcessResponseCpp()
{
int queriesToBeProcessed = NUM_QUERIES_TO_PROCESS;
while (queriesToBeProcessed--) {
// The following call blocks until some response is available in
// the form of a `ResponseMsg`.
ResponseMsg msg;
recvMessage(&msg, serverAddress);
int handle = msg.handle();
QueryResult result = msg.queryResult();
// Process query `result` by applying registered `callBack` to it.
// The `callBack` function is retrieved from the `catalog` using
// the given `handle`.
bsl::function<void(QueryResult)> callBack;
assert(0 == catalog.find(handle, &callBack));
callBack(result);
// Finally, remove the no-longer-needed `callBack` from the
// `catalog`. Assert so that `catalog` may not grow unbounded if
// remove fails.
assert(0 == catalog.remove(handle));
}
}

In some thread, the client executes the following code.

extern "C" void *testClientProcessQuery(void *)
{
testClientProcessQueryCpp();
return 0;
}

In some other thread, the client executes the following code.

extern "C" void *testClientProcessResponse(void *)
{
testClientProcessResponseCpp();
return 0;
}

Example 2: Iterator Usage

The following code fragment shows how to use bdlcc::ObjectCatalogIter to iterate through all the objects of catalog (a catalog of objects of type MyType).

void use(bsl::function<void(QueryResult)> object)
{
(void)object;
}

Now iterate through the catalog:

for (bdlcc::ObjectCatalogIter<MyType> it(catalog); it; ++it) {
bsl::pair<int, MyType> p = it(); // p.first contains the handle and
// p.second contains the object
use(p.second); // the function 'use' uses the
// object in some way
}
// 'it' is now destroyed out of the scope, releasing the lock.
Definition bdlcc_objectcatalog.h:597
Definition bslstl_pair.h:1210
TYPE second
Definition bslstl_pair.h:908

Note that the associated catalog is (read)locked when the iterator is constructed and is unlocked only when the iterator is destroyed. This means that until the iterator is destroyed, all the threads trying to modify the catalog will remain blocked (even though multiple threads can concurrently read the object catalog). So clients must make sure to destroy their iterators after they are done using them. One easy way is to use the for (bdlcc::ObjectCatalogIter<MyType> it(catalog); ... as above.

Function Documentation

◆ operator const void *()

template<class TYPE >
bdlcc::ObjectCatalogIter< TYPE >::operator const void * ( ) const
inline