BDE 4.14.0 Production release
|
Provide a thread-safe object pool.
This component provides a generic thread-safe pool of objects, bdlcc::ObjectPool
, using the acquire-release idiom and a struct
with useful functors for a pool of objects, bdlcc::ObjectPoolFunctors
. An object pool provides two main methods: getObject
, which returns an object from the pool, and releaseObject
, which returns an object to the pool for further reuse (thus avoiding the overhead of object construction and destruction). A major requirement of using the object pool is that any call to getObject
can be satisfied by any object in the pool.
The bdlcc::ObjectPool
class template is fully thread-safe (see {bsldoc_glossary |Fully Thread-Safe}), assuming that the allocator is fully thread-safe. Each method is executed by the calling thread.
The object pool owns the memory required to store the pooled objects, and manages the construction, resetting, and destruction of objects. The user may supply functors to create objects and to reset them to a valid state for their return to the pool. Alternatively, this component supplies reasonable defaults. Upon destruction, the object pool deallocates all memory associated with the objects in the pool.
The object pool also implements the bdlma::Factory
protocol for TYPE. Its createObject
and deleteObject
methods are provided only for this purpose and should not be invoked directly (they are just synonyms for getObject
and releaseObject
, respectively). The pool can thus be used anywhere a bdlma::Factory
(or, therefore, a bdlma::Deleter
) is expected.
A bdlcc::ObjectPool
is designed to work with both managed and shared pointer types. Note however, that bdlcc_sharedobjectpool is an object-pool specifically designed for use with shared pointers.
Because bdlcc::ObjectPool
provides a deleteObject
method, it can serve as a factory of both bslma::ManagedPtr
and bsl::shared_ptr
objects. For example, to create a managed pointer from an object pool of bsl::string
objects:
To create a shared pointer (using the same object pool):
Note that an allocator is a required argument to the bsl::shared_ptr
constructor used here, and the provided allocator is used to supply memory for the internal representation of the pointer, and not to allocate memory for the object itself.
bdlcc::ObjectPool
is templated on two types CREATOR
and RESETTER
in addition to the underlying object TYPE
. Objects of these types may be provided at construction. The namespace bdlcc::ObjectPoolFunctors
provides several commonly used implementations. The creator will be invoked as: void(*)(void*, bslma::Allocator*)
. The resetter will be invoked as: void(*)(TYPE*)
. The creator functor is called to construct a new object of the parameterized TYPE
when the pool must be expanded (and thus it typically invokes placement new
and passes its allocator argument to the constructor of TYPE
). The resetter functor is called before each object is returned to the pool, and is required to put the object into a state such that it is ready to be reused. The defaults for these types are as follows:
bdlcc::ObjectPoolFunctors::Nil
is a no-op; it is only suitable if the objects stored in the pool are always in a valid state to be reused. Otherwise another kind of RESETTER
should be provided. In bdlcc::ObjectPoolFunctors
, the classes Clear
, RemoveAll
, and Reset
are all acceptable types for RESETTER
. Since these functor types are fully inlined, it is generally most efficient to define reset
(or clear
or removeAll
) in the underlying TYPE
and allow the functor to call that method. The CREATOR
functor defaults to an object that invokes the default constructor with placement new, passing the allocator argument if the type traits of the object indicate it uses an allocator (see bslalg_typetraits ). If a custom creator functor or a custom CREATOR
type is specified, it is the user's responsibility to ensure that it correctly passes its allocator argument to the constructor of TYPE
if TYPE
takes an allocator.
There are two potential sources of exceptions in this component: memory allocation and object construction. The object pool is exception-neutral with full guarantee of rollback for the following methods: if an exception is thrown in getObject
, reserveCapacity
, or increaseCapacity
, then the pool is in a valid unmodified state (i.e., identical to its state prior to the call to getObject
). No other method of bdlcc::ObjectPool
can throw.
The growBy
parameter can be specified in the pool's constructor to instruct the pool how to increase its capacity each time the pool is depleted. If growBy
is positive, the pool always replenishes itself with enough objects to satisfy at least growBy
object requests before the next replenishment. If growBy
is negative, the pool will increase its capacity geometrically until it exceeds the internal maximum (which is implementation-defined), and after that it will be replenished with constant number of objects. If growBy
is not specified, it defaults to -1 (i.e., geometric increase beginning at 1).
This section illustrates intended use of this component.
In this example, we simulate a database server accepting queries from clients and executing each query in a separate thread. Client requests are simulated by function getClientQuery
which returns a query to be executed. The class Query
encapsulates a database query and queryFactory
is an object of a query factory class QueryFactory
.
The server runs several threads which, on each iteration, obtain a new client request from the query factory, and process it, until the desired total number of requests is achieved.
We first give an implementation that does not uses the object pool. Later we will give an implementation using an object pool to manage the database connections. We also keep track of total response time for each case. When object pool is not used, each thread, in order to execute a query, creates a new database connection, calls its executeQuery
method to execute the query and finally closes the connection.
The main thread starts and joins these threads:
In above strategy, clients always incur the delay associated with opening and closing a database connection. Now we show an implementation that will use object pool to pool the database connections.
In order to create an object pool, we may specify, at construction time, a functor encapsulating object creation. The pool invokes this functor to create an object in a memory location supplied by the allocator specified at construction and owned by the pool. By default, the creator invokes the default constructor of the underlying type, passing the pool's allocator if the type uses the bslma::Allocator protocol to supply memory (as specified by the "Uses Bslma Allocator" trait, see bslalg_typetraits ). If this behavior is not sufficient, we can supply our own functor for type creation.
When the default constructor of our type is sufficient, whether or not that type uses bslma::Allocator
, we can simply use the default behavior of bdlcc::ObjectPool
:
In this example, if we decide that connection IDs must be supplied to objects allocated from the pool, we must define a function which invokes placement new appropriately. When using a custom creator functor, it is the responsibility of client code to pass the pool's allocator (supplied as the second argument to the functor) to the new object if it uses bslma::Allocator.
then...
Whichever creator we choose, the modified server looks like
Now each thread, instead of creating a new connection, gets a connection from the object pool. After using the connection, the client returns it back to the pool for further reuse. The modified queryHandler
is following.
The total response time for each strategy is: