This is the third article in my mini series about object pooling. Be sure to read part 1 and part 2 first. In this part I’m going to put some of the infrastructure in place that’s needed to handle the list of pooled objects, to make objects available and to allow for them to be put back into the pool.

Initializing the pooled object list

As I don’t want to look at automated object creation just yet, I’ll create a constructor for my pool that allows for a list of pooled objects to be passed in from the outside. The Slot class also needs a constructor that allows me to easily create a Slot for an existing object. These two constructors look like this:

class Slot {
  // ...
  /// <summary>
  /// Constructs a Slot object for a pre-existing pooled type object.
  /// </summary>
  public Slot(T poolObject) {
    if (poolObject == null)
      throw new ArgumentNullException("poolObject");
    this.poolObject = poolObject;
  }
  // ...
}

class Pool {
  // ...
  private Pool( ) {
    pool = new List<Slot>( );
  }

  /// <summary>
  /// Constructs a pool and fills its list of pooled objects with the objects
  /// passed in to this constructor.
  /// </summary>
  public Pool(IEnumerable<T> objects) : this( ) {
    foreach (T entry in objects)
      pool.Add(new Slot(entry));
  }
  // ...
}

As you can see, I’ve added a private constructor to the Pool class that has the purpose of initializing internal fields — I don’t intend to allow the Pool to be created with no parameters at all, so I made that one private.

Getting and releasing objects

I filled the GetObject and ReleaseObject methods with some code that will allow for objects to be allocated and released. To make Pool safe to use from multiple threads, I’m using a lock object. I also introduced a few other helper methods; everything’s here in the code sample:

class Pool {
  // ...
  object poolLock = new object( );

  /// <summary>
  /// Returns a pooled object. After use, the pooled object must be returned to the
  /// pool by calling the ReleaseObject method.
  /// </summary>
  public T GetObject( ) {
    lock (poolLock) {
      Slot unusedSlot = pool.Find(delegate(Slot slot) {
        return !slot.InUse;
      });
      if (unusedSlot != null) {
        unusedSlot.Occupy( );
        return unusedSlot.PoolObject;
      }
    }
    throw new OutOfObjectsException( );
  }

  /// <summary>
  /// Releases an object that was previously fetched from the pool
  /// by a call to the GetObject method.
  /// </summary>
  public void ReleaseObject(T poolObject) {
    lock (poolLock) {
      Slot objectSlot = pool.Find(delegate(Slot slot) {
        return EqualityComparer<T>.Default.Equals(slot.Object, poolObject);
      });
      if (objectSlot != null)
        objectSlot.Release( );
      else
        throw new ArgumentException("The given object is not part of this pool.");
    }
  }
  // ...
}

class Slot {
  // ...
  /// <summary>
  /// Marks the slot as being in use.
  /// </summary>
  public void Occupy( ) {
    inUse = true;
  }

  /// <summary>
  /// Marks the slot as being available.
  /// </summary>
  public void Release( ) {
    inUse = false;
  }
  // ...
}

[Serializable]
public class OutOfObjectsException : Exception {
  public OutOfObjectsException( ) { }
  public OutOfObjectsException(string message) : base(message) { }
  public OutOfObjectsException(string message, Exception innerException) : base(message, innerException) { }
  protected OutOfObjectsException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

Next steps

While the current status is complete in itself and could actually be used to borrow and return objects from the pool, the functionality is severely restricted at this point. The pool doesn’t do enough management work itself, but rather leaves object construction to the outside world. So in the next part I’ll show how the system can be extended to do its own object creation, which will make the pool much better scalable.