Question

Sorry that the question of this problem might be a bit vague. I'm trying to port this ObjectPool code from C# into C++ but seems there are some parts where I don't know how I should proceed. Codes are as follows:

using System;

namespace FastRank
{
    public class ObjectPool<T> where T : class, new()
    {
        private int _count;

        private T[] _pool;

        public ObjectPool(int initSize)
        {
            _pool = new T[initSize];
        }

        public T Get()
        {
            lock (_pool)
            {
                if (_count > 0)
                {
                    --_count;
                    T item = _pool[_count];
                    _pool[_count] = null;
                    return item;
                }
            }
            return new T();
        }

        public void Return(T item)
        {
            lock (_pool)
            {
                if (_count == _pool.Length)
                    Array.Resize(ref _pool, _pool.Length*2 + 1);
                _pool[_count++] = item;
            }
        }
    }
}

My questions are:

1) How should I implement that constraint on generic parameter T in C++? (class, new())

2) Is there a simple way to implement the mutex lock part?

3) Will it be more efficient to define _pool as vector instead of T[] in C++?

edit -> Implemented something as:

#include "object_pool.h"

#include <boost/thread.hpp>
#include <vector>
using namespace std;

template <class T>
ObjectPool<T>::ObjectPool(int init_size) {
  pool_.reserve(init_size);
}

template <class T>
T ObjectPool<T>::Get() {
  boost::lock_guard<boost::mutex> lock(guard_);
  int sz = (int) pool_.size();
  if (sz == 0) {
    throw "Object pool size is now zero.";
  }
  else {
    T item = pool_[sz-1];
    pool_.pop_back();
    return item;
  } 
}

template <class T>
void ObjectPool<T>::Return(T item) {
  boost::lock_guard<boost::mutex> lock(guard_);
  pool_.push_back(item);
}

Wondering if there's any problem with this code...

Was it helpful?

Solution

Here's a naive snippet that illustrates one possible approach:

#include <mutex>

template <typename T>
class SyncStack
{
   T         * m_data;
   std::size_t m_size;
   std::size_t m_count;
   std::mutex  m_lock;

public:
  T get()
  {
    std::lock_guard<std::mutex> lock(m_lock);

    if (m_count == 0) { throw UnderrunException; }

    --m_count;
    T x(m_data[m_count]);
    m_data[m_count].~T();

    return x;
  }

  void put(T x)
  {
    std::lock_guard<std::mutex> lock(m_lock);
    ::new (m_data + m_count) T(std::move(x));
    ++m_count;
  }
};

This example assumes that m_data points to infinite memory. Reallocation is a bit tricky and involves making lots of copies.

A simpler approach would be to wrap your synchronized structure around another, existing standard container such as std::vector<T>.

OTHER TIPS

1) How should I implement that constraint on generic parameter T in C++? (class, new())

In general, don't. If it fails to meet the constraints, it will fail to compile. Simple enough. There are tricky ways to get better error messages, but I've forgotten them offhand because I never bothered.

2) Is there a simple way to implement the mutex lock part?

Use a boost::mutex.

3) Will it be more efficient to define _pool as vector instead of T[] in C++?

Considering that you can't have a local T[] without a size, yes. Use a std::vector. (You can have it as a parameter, but not at the variable definition.)

This is how I would have implemented it. You could replace tbb::concurrenct_queue with a std::mutex guarded std::queue, although that would be less efficient. With this implementation you do need to worry about "returning" objects back to the pool, it's handled automatically.

#include <memory>
#include <tbb/concurrent_queue.h>

namespace FastRank
{
    template<typename T>
    class object_pool
    {
        typedef tbb::concurrent_bounded_queue<std::shared_ptr<T>> pool_t;       
        std::shared_ptr<pool_t> pool_;
    public:

        object_pool() : pool_(new pool_t())
        {
        }

        std::shared_ptr<T> get()
        {
            std::shared_ptr<T> ptr;
            if(!pool_.try_pop(ptr))
                ptr = std::make_shared<T>();

            auto pool = pool_;
            return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*){pool->push(ptr);});
        }
    }
}

Without concurrent_queue

#include <memory>
#include <queue>
#include <boost/mutex.hpp>

namespace FastRank
{
    template<typename T>
    class object_pool
    {
        typedef std::pair<std::queue<std::shared_ptr<T>>, boost::mutex> pool_t;     
        std::shared_ptr<pool_t> pool_;
    public:

        object_pool() : pool_(new pool_t())
        {
        }

        std::shared_ptr<T> get()
        {
            std::shared_ptr<T> ptr;
            {
                boost::scoped_lock<boost::mutex> lock(pool_->second);
                if(!pool_->first.empty())
                {
                     ptr = std::move(pool->first.front());
                     pool->first.pop()
                }
            }

            if(!ptr)
                ptr = std::make_shared<T>();

            auto pool = pool_;
            return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*)
            {
                boost::scoped_lock<boost::mutex> lock(pool->second);                    
                pool->push(ptr);
            });
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top