blob: f377e0abfb432f4d08b4239d705a040d953859e8 [file] [log] [blame]
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
#ifndef TMPOOL_H_
#define TMPOOL_H_
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include "seabed/ms.h"
#include "seabed/trace.h"
#include "tmlogging.h"
#include "tmmap.h"
#include "tmdeque.h"
#include "tmtime.h"
#include "tmlibmsg.h"
// EID state values
#define EID_STATE_DELETED 'X'
#define EID_STATE_INUSE 'U'
#define EID_STATE_FREE 'F'
// CTmPool class definition
// This class encapsulates the generalized TM pool
// T is the class type for the elements contained in the pool. For example, the
// transaction object pool uses class TM_TX_Info.
// Notes:
// 1. Elements must be initialized once returned by a call to CTmPool::newElement.
// 2. Element classes must externalize a cleanup() method as this is called by deleteElement.
template<class T> class CTmPool
{
private:
TM_MAP *ip_inUseList;
TM_DEQUE *ip_freeList;
// Configuration
int32 iv_max; // Maximum size for pool
int32 iv_steadyLow; // Below this new requests always create a new element.
int32 iv_steadyHigh; // Above this deleted elements are not returned to
// the free list.
// Statistics
bool iv_collectStats; // True = collect stats.
// Set by DTM_TM_STATS=1 registry value, default is TM_STATS.
Ctimeval iv_startTime; // Time since pool was instantiated
Ctimeval iv_lastTimeInterval; // Time since last timeSinceLastCumStats() call.
int32 iv_poolThresholdEventCounter;
int32 iv_total; // Total elements in in-use and free lists.
// Total values. This is the total number since startup/reset.
int32 iv_totalAllocs_new; // Allocations instantiating a new object.
int32 iv_totalAllocs_free; // Allocations reusing elements from the free list.
int32 iv_totalDeallocs_free; // Deallocations returned to free list.
int32 iv_totalDeallocs_delete; // Deallocations deleting object.
int32 iv_perSecCum_allocs_new; // Cumulative allocations per sec
int32 iv_perSecCum_allocs_free; // Cumulative allocations per sec
int32 iv_perSecCum_deallocs_free; // Cumulative deallocations per sec
int32 iv_perSecCum_deallocs_delete; // Cumulative deallocations per sec
int32 iv_perSecCumSq_allocs_new; // Cumulative squares of allocations per sec
int32 iv_perSecCumSq_allocs_free; // Cumulative squares of allocations per sec
int32 iv_perSecCumSq_deallocs_free; // Cumulative squares of deallocations per sec
int32 iv_perSecCumSq_deallocs_delete;// Cumulative squares of deallocations per sec
TM_Mutex iv_mutex;
public:
//----------------------------------------------------------------------------
// CTmPool methods
// All CTmPool methods must be placed in the header file because
// the compiler can't cope with them being in a .cpp!
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// CTmPool Constructor
//----------------------------------------------------------------------------
CTmPool(bool pv_collectStats, int32 pv_max, int32 pv_steadyLow,
int32 pv_steadyHigh)
: iv_max(pv_max), iv_steadyLow(pv_steadyLow),
iv_steadyHigh(pv_steadyHigh),
iv_collectStats(pv_collectStats),
iv_poolThresholdEventCounter(0), iv_total(0),
iv_perSecCum_allocs_new(0), iv_perSecCum_allocs_free(0),
iv_perSecCum_deallocs_free(0), iv_perSecCum_deallocs_delete(0),
iv_perSecCumSq_allocs_new(0), iv_perSecCumSq_allocs_free(0),
iv_perSecCumSq_deallocs_free(0), iv_perSecCumSq_deallocs_delete(0)
{
char la_buf[DTM_STRING_BUF_SIZE];
ip_inUseList = new TM_MAP();
ip_freeList = new TM_DEQUE();
resetCounters();
if (iv_max < iv_steadyHigh ||
iv_steadyHigh < iv_steadyLow ||
iv_steadyLow < 0)
{
// EMS DTM_BAD_PARAM
sprintf(la_buf, "CTmPool Fatal error. Pool configuration "
"parameter mismatch. steadyLow %d, steadyHigh %d, max %d\n",
iv_steadyLow, iv_steadyHigh, iv_max);
//TMTrace(1, ("CTmPool<class T>::CTmPool : %s", la_buf));
tm_log_write(DTM_BAD_PARAM, SQ_LOG_CRIT, la_buf);
abort();
}
//TMTrace(2, ("CTmPool<class T>::CTmPool : EXIT.\n"));
} //CTmPool<class T>::CTmPool
// --------------------------------------------------------------
// CTmPool<class T> Destructor
// --------------------------------------------------------------
~CTmPool()
{
//TMTrace(2, ("CTmPool<class T>::~CTmPool : ENTRY/EXIT for pool %p\n", (void *) this));
delete ip_inUseList;
delete ip_freeList;
}
int elementSize() {return sizeof(T);}
TM_MAP *get_inUseList() {return ip_inUseList;}
TM_DEQUE *get_freeList() {return ip_freeList;}
int32 get_maxPoolSize() {return iv_max;}
int32 get_ssHigh() {return iv_steadyHigh;}
int32 get_ssLow() {return iv_steadyLow;}
int32 totalElements() {return iv_total;}
void dec_totalElements() {
lock();
iv_total--;
unlock();
}
void set_maxPoolSize(int32 pv_newMax)
{
lock();
iv_max=pv_newMax;
unlock();
}
void set_ssHigh(int32 pv_new)
{
lock();
iv_steadyHigh=pv_new;
unlock();
}
void set_ssLow(int32 pv_new)
{
lock();
iv_steadyLow=pv_new;
unlock();
}
// ------------------------------------------------------------
// CTmPool<class T>::get
// Purpose : find an element in the inUseList and return it
// ------------------------------------------------------------
T * get(int64 pv_index)
{
return (T *) ip_inUseList->get(pv_index);
}
// ------------------------------------------------------------
// CTmPool<class T>::getFirst_inUseList
// Purpose : find the first element in the inUseList and return it
// ------------------------------------------------------------
T * getFirst_inUseList()
{
return (T *) ip_inUseList->get_first();
}
// ------------------------------------------------------------
// CTmPool<class T>::getNext_inUseList
// Purpose : find the next element in the inUseList and return it
// ------------------------------------------------------------
T * getNext_inUseList()
{
return (T *) ip_inUseList->get_next();
}
// ------------------------------------------------------------
// CTmPool<class T>::getEnd_inUseList
// Purpose : Unlock the inUseList when we've finished. Must be called
// by any code which calls getFirst_inUseList.
// ------------------------------------------------------------
void getEnd_inUseList()
{
ip_inUseList->get_end();
}
// ------------------------------------------------------------
// CTmPool<class T>::getFirst_freeList
// Purpose : find the first element in the freeList and return it
// ------------------------------------------------------------
T * getFirst_freeList()
{
ip_freeList->lock();
return (T *) ip_freeList->get_firstFIFO();
}
// ------------------------------------------------------------
// CTmPool<class T>::getNext_freeList
// Purpose : find the next element in the freeList and return it
// ------------------------------------------------------------
T * getNext_freeList()
{
return (T *) ip_freeList->get_nextFIFO();
}
// ------------------------------------------------------------
// CTmPool<class T>::getEnd_freeList
// Purpose : Unlock the freeList when we've finished. Must be called
// by any code which calls getFirst_freeList.
// ------------------------------------------------------------
void getEnd_freeList()
{
ip_freeList->unlock();
}
//----------------------------------------------------------------------------
// CTmPool<class T>::newElement
// Purpose : returns an element either from the free list, or instantiates one
// depending on availability (free list) and the configuration. This and in
// deleteElement is where the pool allocation algorithm is defined.
// T will be the class type of the element to be instantiated.
// pv_index is the next available index number.
// if pv_reuseIndex is true, pv_index will be used only if allocating a
// new element.
// if pv_reuseIndex is false, pv_index will always be used as the index to
// the new element.
// pp_reused is returned true of an element was reused from the freeList.
// constructPoolElement is a pointer to the function to be called if supplied. This is used
// to call the static function constructPoolElement in a derived class when
// 2 or more classes derive from the base class that implements the CTmPoolElement.
//----------------------------------------------------------------------------
T * newElement(int64 pv_index, bool * pp_reused, bool pv_reuseIndex=true,
void * (*constructPoolElement)(int64) =NULL)
{
int64 lv_index = pv_index;
char la_buf[DTM_STRING_BUF_SIZE];
T *lp_element = NULL;
//TMTrace (2, ("CTmPool<class T>::newElement : ENTRY index " PFLL ".\n", pv_index));
lock();
// If we have less elements than the steady state low, then
// instantiate a newElement one. Otherwise try to get one from the free list.
if (iv_total >= iv_steadyLow)
{
lp_element = (T *) ip_freeList->pop();
// If we got a NULL pointer back from pop() then freeList is empty.
// Retrieve the thread number and reuse it
if (lp_element)
{
lv_index = lp_element->cleanPoolElement();
//TMTrace (3, ("CTmPool<class T>::newElement : Element %p allocated from Free list,"
// " total elements %d, inUseList size " PFLL ", freeList size " PFLL "\n",
// (void *) lp_element, iv_total, ip_inUseList->size(), ip_freeList->size()));
}
}
// Don't allow more than iv_max elements to be instantiated
if (lp_element == NULL && iv_total >= iv_max)
{
unlock();
*pp_reused = false;
//sprintf(la_buf, "CTmPool Exceeded the maximum elements of %d.\n", iv_max);
//tm_log_write(DTM_TMPOOL_MAX_EXCEEDED, SQ_LOG_CRIT, la_buf);
//TMTrace (1, ("CTmPool<class T>::newElement : Exceeded maximum elements of %d"
// ", inUseList size " PFLL ", freeList size " PFLL "\n",
// iv_max, ip_inUseList->size(), ip_freeList->size()));
return NULL;
}
// Instantiate a newElement element if we didn't get one from the freeList
// Note that elements must be initialized by the caller after calling CTmPool<class T>::newElement.
if (lp_element == NULL)
{
// If the constructPoolElement function pointer was passed in use it
if (constructPoolElement != NULL)
lp_element = (T *) constructPoolElement(lv_index);
else
lp_element = T::constructPoolElement(lv_index);
iv_totalAllocs_new++;
*pp_reused = false;
iv_total++;
//TMTrace (3, ("CTmPool<class T>::newElement : newElement element %p instantiated, "
// "total elements %d, inUseList size " PFLL ", freeList size " PFLL ".\n",
// (void *) lp_element, iv_total, ip_inUseList->size(), ip_freeList->size()));
// Log an event because we're over the steady state (> iv_steadyHigh elements)
// We only write an event every 5 times to try to avoid flooding the log.
if (iv_total > iv_steadyHigh &&
(iv_poolThresholdEventCounter++ % 5) == 0)
{
sprintf(la_buf, "TmPool %p is approaching its maximum size of %d. Currently %d elements.\n",
(void *) this, iv_max, iv_total);
tm_log_write(DTM_TMPOOL_THRESHOLD_EXCEEDED, SQ_LOG_WARNING, la_buf);
//TMTrace (3, ("CTmPool<class T>::newElement - %s",la_buf));
}
}
else
{
// Reuse the element from the free list.
iv_totalAllocs_free++;
*pp_reused = true;
// If we're not reusing the index, then reset to the supplied index now.
if (pv_reuseIndex == false)
lv_index = pv_index;
//TMTrace (3, ("CTmPool<class T>::newElement : reusing element %p "
// "total elements %d, inUseList size " PFLL ", freeList size " PFLL ".\n",
// (void *) lp_element, iv_total, ip_inUseList->size(), ip_freeList->size()));
}
ip_inUseList->put(lv_index, lp_element);
lp_element->EIDState(EID_STATE_INUSE);
unlock();
//TMTrace (2, ("CTmPool<class T>::newElement : EXIT, index " PFLL ", element %p.\n", lv_index, (void *) lp_element));
return lp_element;
} //CTmPool<class T>::newElement
//----------------------------------------------------------------------------
// CTmPool<class T>::deleteElement
// Purpose : returns an element on the inUseList to the freeList or deletes it
// depending on number of elements allocated and the configuration.
// deleteElement uses pv_index to find the element in the inUseList.
//----------------------------------------------------------------------------
bool deleteElement(int64 pv_index)
{
bool lv_delete = false;
T *lp_element = NULL;
char la_buf[DTM_STRING_BUF_SIZE];
//TMTrace (2, ("CTmPool<class T>::deleteElement : ENTRY index " PFLL ".\n", pv_index));
lock();
lp_element = (T *) ip_inUseList->get(pv_index);
if (lp_element)
ip_inUseList->remove(pv_index);
else
{
unlock();
// EMS DTM_BAD_PARAM
sprintf(la_buf, "CTmPool::deleteElement : Logic Error. Trying to deleteElement "
"" PFLL " not found in inUseList.\n",
pv_index);
//TMTrace(1, ("CTmPool: %s", la_buf));
tm_log_write(DTM_LOGIC_ERROR, SQ_LOG_CRIT, la_buf);
abort();
}
// Delete the element if we're above the steady state high
if (iv_total > iv_steadyHigh)
{
//TMTrace (3, ("CTmPool<class T>::deleteElement : Above steady state high, deleting element %p. "
// "Total elements %d, inUseList size " PFLL ", freeList size " PFLL ".\n",
// (void *) lp_element, iv_total, ip_inUseList->size(), ip_freeList->size()));
iv_total--;
lv_delete = true;
iv_totalDeallocs_delete++;
lp_element->EIDState(EID_STATE_DELETED);
}
else
{
// Below the steady state high, put it on the freeList
//TMTrace (3, ("CTmPool<class T>::deleteElement : Below steady state high, returning "
// "element %p to freeList. "
// "Total elements %d, inUseList size " PFLL ", freeList size " PFLL ".\n",
// (void *) lp_element, iv_total, ip_inUseList->size(), ip_freeList->size()));
ip_freeList->push(lp_element);
iv_totalDeallocs_free++;
lp_element->EIDState(EID_STATE_FREE);
}
unlock();
// We delay the delete to here so that we can unlock the pool mutex prior to the delete.
// For thread objects the delete will stop execution!
if (lv_delete)
delete lp_element;
//TMTrace (2, ("CTmPool<class T>::deleteElement : EXIT index " PFLL ", element %p deleted.\n",
// pv_index, (void *) lp_element));
return lv_delete;
} //CTmPool<class T>::deleteElement
//----------------------------------------------------------------------------
// CTmPool<class T>::setConfig
// Purpose : Set the pool configuration.
// It is intended that this will (eventually) allow dynamic reconfiguration
// of TmPools.
// pv_collectStats must always be provided.
// Other parameters are set only if the value is >= 0.
// Returns true if successful.
//----------------------------------------------------------------------------
bool setConfig(bool pv_collectStats, int32 pv_max=-1, int32 pv_steadyLow=-1,
int32 pv_steadyHigh=-1)
{
bool lv_return = false;
char la_buf[DTM_STRING_BUF_SIZE];
int32 lv_max = (pv_max >= 0)?pv_max:iv_max;
int32 lv_steadyLow = (pv_steadyLow >= 0)?pv_steadyLow:iv_steadyLow;
int32 lv_steadyHigh = (pv_steadyHigh >= 0)?pv_steadyHigh:iv_steadyHigh;
//TMTrace (2, ("CTmPool<class T>::setConfig : ENTRY setting collectStats %d, "
// "max %d, steadyLow %d, steadyHigh %d\n",
// pv_collectStats, pv_max, pv_steadyLow, pv_steadyHigh));
lock();
if (lv_max < lv_steadyHigh ||
lv_steadyHigh < lv_steadyLow ||
lv_steadyLow < 0)
{
// EMS DTM_BAD_PARAM
sprintf(la_buf, "CTmPool Configuration error. Parameter mismatch. "
"Configuration change ignored. steadyLow %d, steadyHigh %d, max %d\n",
lv_steadyLow, lv_steadyHigh, lv_max);
//TMTrace(1, ("CTmPool<class T>::setConfig : %s", la_buf));
tm_log_write(DTM_BAD_PARAM, SQ_LOG_WARNING, la_buf);
}
else
{
iv_collectStats = pv_collectStats;
iv_max = lv_max;
iv_steadyLow = lv_steadyLow;
iv_steadyHigh = lv_steadyHigh;
lv_return = true;
}
unlock();
//TMTrace (2, ("CTmPool<class T>::setConfig : EXIT returning %d.\n", lv_return));
return lv_return;
} //CTmPool<class T>::setConfig
//----------------------------------------------------------------------------
// CTmPool<class T>::timeSinceLastCumStats
// Purpose : Calculate the number of seconds since the last call.
//----------------------------------------------------------------------------
int timeSinceLastCumStats()
{
int lv_return = 0;
Ctimeval lv_now = Ctimeval::now();
//TMTrace(2, ("CTmPool<class T>::timeSinceLastCumStats : ENTRY.\n"));
lv_return = lv_now.tv_sec - iv_lastTimeInterval.tv_sec;
iv_lastTimeInterval = lv_now;
//TMTrace(2, ("CTmPool<class T>::timeSinceLastCumStats : EXIT returning %d secs "
// "since last call.\n", lv_return));
return lv_return;
} //CTmPool<class T>::timeSinceLastCumStats
//----------------------------------------------------------------------------
// CTmPool<class T>::resetCounters
//----------------------------------------------------------------------------
void resetCounters()
{
Ctimeval lv_now = Ctimeval::now();
lock();
iv_startTime = lv_now;
iv_lastTimeInterval = lv_now;
iv_totalAllocs_new = 0;
iv_totalAllocs_free = 0;
iv_totalDeallocs_free = 0;
iv_totalDeallocs_delete = 0;
unlock();
} //CTmPool<class T>::resetCounters
// --------------------------------------------------------------
// CTmPool<class T>::lock
// Lock the mutex to serialize access to the object
// --------------------------------------------------------------
void lock()
{
//TMTrace (5, ("CTmPool<class T>::lock(%d)\n", iv_lock_count));
iv_mutex.lock();
}
// --------------------------------------------------------------
// CTmPool<class T>::unlock
// Unlock the mutex to serialize access to the object
// --------------------------------------------------------------
void unlock()
{
//TMTrace (5, ("CTmPool<class T>::unlock(%d)\n", iv_lock_count));
iv_mutex.unlock();
}
// --------------------------------------------------------------
// CTmPool<class T>::getPoolStats
// Retrieves the pool stats for this pool
// --------------------------------------------------------------
void getPoolStats(TMPOOLSTATS *pp_stats)
{
//TMTrace (2, ("CTmPool<class T>::getPoolStats ENTRY.\n"));
lock();
pp_stats->iv_lastTimeInterval = timeSinceLastCumStats();
pp_stats->iv_poolSizeNow = iv_total;
pp_stats->iv_inUseListNow = ip_inUseList->size();
pp_stats->iv_freeListNow = ip_freeList->size();
pp_stats->iv_steadyStateLow = iv_steadyLow;
pp_stats->iv_steadyStateHigh = iv_steadyHigh;
pp_stats->iv_max = iv_max;
pp_stats->iv_poolThresholdEventCounter = iv_poolThresholdEventCounter;
CdblTime lv_start(iv_startTime);
pp_stats->iv_startTime = lv_start.get();
pp_stats->iv_totalAllocs_free = iv_totalAllocs_free;
pp_stats->iv_totalAllocs_new = iv_totalAllocs_new;
pp_stats->iv_totalDeallocs_delete = iv_totalDeallocs_delete;
pp_stats->iv_totalDeallocs_free = iv_totalDeallocs_free;
unlock();
} //CTmPool<class T>::getPoolStats
}; //class CTmPool
#endif //TMPOOL_H_