Click here to Skip to main content
15,896,497 members
Articles / Programming Languages / C++
Article

Generic Lookaside List Class

Rate me:
Please Sign up or sign in to vote.
3.00/5 (1 vote)
12 Oct 2000 51.7K   469   13  
A simple way to keep items such as COM instances 'warm' and available for reuse
  • Download source files - 2 Kb
  • Introduction

    I recently had a need to maintain a 'warm' list of objects. In my case, these were ADO RecordsetPtr and CommandPtr objects. The code I was working on had a large number of routines that created a RecordsetPtr, executed an SQL command, looked at the results, and deleted the RecordsetPtr. These routines were called again and again, but in no simple pattern.

    In looking at the performance traces, I found that a surprising amount of time was spent creating the COM object instances, then tearing them down. I quickly decided that what I needed was a simple way to keep these COM instances 'warm' and available for reuse, and that such a mechanism should change the existing code as little as possible.

    Obviously, what I needed was a lookaside list. I was surprised when I didn't find anything that quite fit the bill, and decided to build a relatively simple generic lookaside list handler.

    The result consists of two template classes: lookaside, which manages the set of warm objects, and simpleIniter, which is used to perform object-specific initialization and teardown functions (remember that my goal was to minimize changes elsewhere in the code, so I decided against forcing each contained object into a wrapper class).

    A lookaside instance accepts two template arguments in its declaration: the type of the object to be stored, and the 'initer' class, which is used to perform type specific initialization and teardown operations (see below). The constructor also accepts two arguments: the number of objects to pre-load into the lookaside list, and the maximum number of objects to ever hold in it.

    lookaside has two main methods, Get and Release, along with a small number of supporting methods. Together, these are:

    • Get returns a pointer to a warm object, and will have created one, if necessary.
    • Release returns an object to the lookaside list, and may delete the object.
    • Drain deletes all objects currently held in the lookaside list.
    • GetCurSize returns the number of objects currently held in the lookaside list.
    • GetMaxSize returns the maximum number of objects that may be held in this lookaside list.

    The base code also includes a default 'initer' class, simpleIniter which perform effectively null operations for each of its four methods. These are:

    • init is called just after the new operation that created the object
    • activate is called just before returning the object to the consumer (via lookaside::Get)
    • deactivate is called just after the object is returned to the lookaside list via lookaside::Release
    • deinit is called just before the delete operation to destory the object.

    Normally all of these routines will be effectively null, and simpleIniter covers these cases. However, as an example showing when these methods may be useful, consider my initial reason for building this class: maintaining a lookaside list for ADO objects. In that case, I built the following variant called ADOIniter:

    template<class T>
    class ADOIniter
    {
    public:
                        ADOIniter ()        {};
    
        bool            init (T *p);
        bool            activate (T *p)     {return true;};
        bool            deactivate (T *p)   {return true;};
        void            deinit (T *p)       {return; };
    };

    This class varies from simpleIniter inasmuch as it has an active init method, which is used to create the appropriate instance, e.g.:

    bool ADOIniter<_CommandPtr>::init(_CommandPtr *p)
    {
        return SUCCEEDED (p -> CreateInstance (__uuidof (Command)));
    };
    
    bool ADOIniter<_RecordsetPtr>::init(_RecordsetPtr *p)
    {
        return SUCCEEDED (p -> CreateInstance (__uuidof (Recordset)));
    };

    These methods ensured that any object returned from the Get function was ready for use, regardless whether it had just been created or not.

    I've included the source for the lookaside and simpleIniter classes below, and they can also be found at the download point listed above.

    // Copyright (c): 2000, Software Exploration, Inc.
    //
    // This file is provided "as is" with no expressed or implied warranty.
    // The author accepts no liability for any damage/loss of business that
    // this product may cause.
    //
    // This code may be used in compiled form in any way you desire. This
    // file may be redistributed unmodified by any means PROVIDING it is
    // not sold for profit without the author's written consent, and
    // providing that this notice, the contact information, and all copyright
    // notices remains intact. If the source code in this file is used in
    // any  commercial application then a statement along the lines of
    // "Portions copyright (c): 2000, Software Exploration, Inc" must be
    // included in the startup banner, "About" box or printed documentation.
    //
    //
    // Contact Information:
    //      Author:             James E Johnson
    //      email:              Jim.Johnson@software-exploration.com
    //      website:            http://www.software-exploration.com
    //      source location:    ftp://downloads.software-exploration.com/lookaside.zip
    //
    
    // Description:
    //
    //
    //
    
    #ifndef _LOOKASIDE_H_
    #define _LOOKASIDE_H_
    
    #include <list>
    
    // simpleIniter: Simple initialization/deactivation worker class
    //
    //  This class handles the initialization/activation/deactivation/teardown of
    //  simple objects that need no other setup or teardown aside from new & delete.
    //
    //  More complex objects should implement the equivalent class interface and
    //  use it in their declaration of a lookaside object.
    //
    // Interface:
    //
    //  This class has four methods that are used by the lookaside class:
    //
    //      init:       this method is called post-new, but before any decision has
    //                  been made to return the object to a consumer.
    //
    //      activate:   this method is called just before returning the object in a
    //                  Get call.
    //
    //      deactivate: This method is called at the start of a Release call.
    //
    //      deinit:     This method is called just before the object is deleted.
    //
    
    template<class T>
    class simpleIniter
    {
    public:
                        simpleIniter ()     {};
    
        bool            init (T *p)         {return true;};
        bool            activate (T *p)     {return true;};
        bool            deactivate (T *p)   {return true;};
        void            deinit (T *p)       {return; };
    };
    
    // lookaside: Generic Lookaside list handler
    //
    //  This class implements a simple, generic lookaside list manager.  It attempts
    //  to keep a set of 'warm' objects ready for use, with limits on how many to
    //  maintain ready for use.
    //
    // Interface:
    //
    //  This class has the following interface:
    //
    //      (construction): lookaside foo (min, max)
    //      The constructor can accept a maximum number of 'warm' objects to keep
    //      around, and the minimum number of 'warm' objects to activate in the
    //      constructor.
    //
    //      (destruction):  ~lookaside
    //      No parameters.  Destroys any 'warm' objects currently held on the
    //      lookaside list.
    //
    //      Retrieval interfaces:
    //
    //      Get:            This returns a pointer to an object that is available
    //                      for use, or a null pointer, if an object could not be
    //                      made available.
    //
    //      Release (ptr, DontReuse):
    //                      Return control of an object to the lookaside list.  If
    //                      the DontReuse flag is true, the object is deleted rather
    //                      than made available for a later consumer.
    //
    //      Drain:          Delete all objects currently held 'warm' in the lookaside
    //                      list.
    //
    //      Informational interfaces:
    //
    //      GetCurSize:     Returns the number of items currently held 'warm' in the
    //                      lookaside list.
    //
    //      GetMaxSize:     Returns the maximum number of items that may be held 'warm'
    //                      in the list.
    //
    //
    template<class T, class A = simpleIniter<T> >
    class lookaside
    {
    public:
                        lookaside (int nMin = 0, int nMax = 1)
                        {
                            m_nMax = nMax;
                            if (nMin > nMax) nMin = nMax;
    
                            FillToMin (nMin);
                        };
    
        virtual         ~lookaside ()
                        {
                            Drain ();
                        }
    
        T               *Get (void)
                        {
                            T   *pRes;
                            if (!m_lstFreeElems.empty ())
                            {
                                pRes = m_lstFreeElems.front ();
                                m_lstFreeElems.pop_front ();
                            }
                            else
                                pRes = NewElem ();
    
                            if (!m_Initer.activate (pRes))
                            {
                                Release (pRes, true);
                                pRes = NULL;
                            }
    
                            return pRes;
                        };
    
        void            Release (T *p, bool fDontReuse = false)
                        {
                            fDontReuse |= !m_Initer.deactivate (p);
                            if (fDontReuse)     DeleteEntry (p);
                            else                m_lstFreeElems.push_back (p);
    
                            while (m_lstFreeElems.size () > m_nMax)
                            {
                                T *p = m_lstFreeElems.front ();
                                m_lstFreeElems.pop_front ();
                                DeleteEntry (p);
                            }
                        };
    
        void            Drain (void)
                        {
                            while (!m_lstFreeElems.empty ())
                            {
                                T *p = m_lstFreeElems.front ();
                                m_lstFreeElems.pop_front ();
                                DeleteEntry (p);
                            }
                        };
    
        int             GetCurSize () const     {return m_lstFreeElems.size (); };
        int             GetMaxSize () const     {return m_nMax; };
    
    private:
        int             m_nMax;                 // Maximum allowable size of the list
    
        std::list<T *>  m_lstFreeElems;         // Warm object list
        A               m_Initer;               // The contained initializer object
    
        // Allocate and initialize a new object.
        //
        T               *NewElem ()
                        {
                            T *p = new T;
                            m_Initer.init (p);
    
                            return p;
                        };
    
        // Fill the lookaside list to at least the level supplied by nMin.
        //
        void            FillToMin (int nMin)
                        {
                            while (m_lstFreeElems.size () < nMin)
                            {
                                T   *p = NewElem ();
                                m_lstFreeElems.push_back (p);
                            }
                        };
    
        // de-initialize and delete a warm object
        //
        void            DeleteEntry (T *p)
                        {
                            m_Initer.deinit (p);
                            delete p;
                        };
    };
    
    #endif // _LOOKASIDE_H_

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    United States United States
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    -- There are no messages in this forum --