blob: 86d9a3af95d73acddf401112e0ad1febc6796fd8 [file] [log] [blame] [raw]
/*
copyright: Boaz segev, 2015
license: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#include "http-objpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
/////////////////////////////////////////////////////////
// The ObjectPool
struct ObjectContainer {
void* object;
struct ObjectContainer* next;
};
struct ObjectPool {
pthread_mutex_t lock;
struct ObjectContainer* objects;
struct ObjectContainer* containers;
void* (*create)(void);
void (*destroy)(void* object);
int object_count;
int is_waiting;
int wait_in;
int wait_out;
};
/////////////////////////////////////////////////////////
// The API
/**
Returns the (approximate) number of objects available the pool.
*/
int pool_count(object_pool pool) {
return pool->object_count;
}
/**
Grabs an object from the pool, removing it from the pool's
registry.
The object will be removed from the pool until returned using `push`.
*/
static void* pop(object_pool pool) {
pthread_mutex_lock(&pool->lock);
void* object = NULL;
struct ObjectContainer* c = pool->objects;
if (c) {
// we have an availavle object.
object = c->object;
// step the objects pool forward
pool->objects = c->next;
// move the object's container to the container pool
c->next = pool->containers;
pool->containers = c;
// update the object count
pool->object_count--;
// unlock
pthread_mutex_unlock(&pool->lock);
// return the object
return object;
} else {
if (pool->wait_in) {
// this is a blocking object pool - update waiting count
pool->is_waiting++;
pthread_mutex_unlock(&pool->lock);
// time to block
read(pool->wait_out, &(c), 1);
return pop(pool);
} else {
// reset the object count
pool->object_count = 0;
// this is a dynamic object pool - create a new object.
pthread_mutex_unlock(&pool->lock);
return pool->create();
}
}
return 0;
}
/**
Returns an object (or pushes a new object) to the pool, making it available
for future `pop` calls.
*/
static void push(object_pool pool, void* object) {
pthread_mutex_lock(&pool->lock);
struct ObjectContainer* c = pool->containers;
if (c) {
pool->containers = c->next;
} else {
c = malloc(sizeof(struct ObjectContainer));
}
c->next = pool->objects;
c->object = object;
pool->objects = c;
// update the object count
pool->object_count++;
// send a signal if someone is waiting
if (pool->is_waiting) {
write(pool->wait_in, &c, 1);
pool->is_waiting--;
}
pthread_mutex_unlock(&pool->lock);
}
/**
Initialize a new ObjectPool that grows when there aren't available objects.
void* (*create)(void* arg):: a callback that returns a new object instance.
void* (*destroy)(void* object):: a callback that destroys an object.
void* arg:: a pointer that will be passed to the `create` callback.
size:: the (initial) number of items in the pool.
*/
static void* new_dynamic(void* (*create)(void),
void (*destroy)(void* object),
int size) {
if (!create)
return NULL;
struct ObjectPool* pool = malloc(sizeof(struct ObjectPool));
pool->wait_in = pool->wait_out = pool->is_waiting = 0;
pool->object_count = 0;
pool->objects = NULL;
pool->containers = NULL;
pool->create = create;
pool->destroy = destroy;
if (!destroy)
pool->destroy = free;
pthread_mutex_init(&pool->lock, NULL);
// create initial pool
while (size--)
push(pool, create());
// return the pool object
return pool;
}
/**
Initialize a new ObjectPool that blocks and waits when there aren't any
available objects.
void* (*create)(void* arg):: a callback that returns a new object instance.
void* (*destroy)(void* object):: a callback that destroys an object.
void* arg:: a pointer that will be passed to the `create` callback.
size:: the (initial) number of items in the pool.
*/
static void* new_blocking(void* (*create)(void),
void (*destroy)(void* object),
int size) {
if (!create)
return NULL;
int io[2];
if (pipe(io))
return NULL;
struct ObjectPool* pool = malloc(sizeof(struct ObjectPool));
pool->object_count = 0;
pool->is_waiting = 0;
pool->wait_in = io[0];
pool->wait_out = io[1];
pool->objects = NULL;
pool->containers = NULL;
pool->create = create;
pool->destroy = destroy;
if (!destroy)
pool->destroy = free;
// create initial pool
while (size--)
push(pool, create());
// return the pool object
return pool;
}
/**
Destroys the pool object and any items in the pool.
*/
static void destroy(object_pool pool) {
if (!pool)
return;
struct ObjectContainer* c;
while (pool->containers) {
c = pool->containers;
pool->containers = c->next;
free(c);
}
while (pool->objects) {
c = pool->objects;
pool->objects = c->next;
pool->destroy(c->object);
free(c);
}
pool->is_waiting = 0;
if (pool->wait_in)
close(pool->wait_in);
if (pool->wait_out)
close(pool->wait_out);
pthread_mutex_destroy(&pool->lock);
free(pool);
return;
}
struct ObjectPool_API__ ObjectPool = {
.new_dynamic = new_dynamic,
.new_blocking = new_blocking,
.destroy = destroy,
.push = push,
.pop = pop,
.count = pool_count,
};