Merge Trond.
[m6w6/libmemcached] / libmemcachedutil / memcached_pool.c
1 #include "libmemcached/common.h"
2 #include "libmemcached/memcached_pool.h"
3
4 #include <errno.h>
5 #include <pthread.h>
6
7 struct memcached_pool_st
8 {
9 pthread_mutex_t mutex;
10 pthread_cond_t cond;
11 memcached_st *master;
12 memcached_st **mmc;
13 int firstfree;
14 uint32_t size;
15 uint32_t current_size;
16 };
17
18 static memcached_return mutex_enter(pthread_mutex_t *mutex)
19 {
20 int ret;
21 do
22 ret= pthread_mutex_lock(mutex);
23 while (ret == -1 && errno == EINTR);
24
25 return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS;
26 }
27
28 static memcached_return mutex_exit(pthread_mutex_t *mutex) {
29 int ret;
30 do
31 ret= pthread_mutex_unlock(mutex);
32 while (ret == -1 && errno == EINTR);
33
34 return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS;
35 }
36
37 /**
38 * Grow the connection pool by creating a connection structure and clone the
39 * original memcached handle.
40 */
41 static int grow_pool(memcached_pool_st* pool) {
42 memcached_st *obj= calloc(1, sizeof(*obj));
43 if (obj == NULL)
44 return -1;
45
46 if (memcached_clone(obj, pool->master) == NULL)
47 {
48 free(obj);
49 return -1;
50 }
51
52 pool->mmc[++pool->firstfree] = obj;
53 pool->current_size++;
54
55 return 0;
56 }
57
58 memcached_pool_st *memcached_pool_create(memcached_st* mmc,
59 uint32_t initial, uint32_t max)
60 {
61 memcached_pool_st* ret = NULL;
62 memcached_pool_st object = { .mutex = PTHREAD_MUTEX_INITIALIZER,
63 .cond = PTHREAD_COND_INITIALIZER,
64 .master = mmc,
65 .mmc = calloc(max, sizeof(memcached_st*)),
66 .firstfree = -1,
67 .size = max,
68 .current_size = 0 };
69
70 if (object.mmc != NULL)
71 {
72 ret= calloc(1, sizeof(*ret));
73 if (ret == NULL)
74 {
75 free(object.mmc);
76 return NULL;
77 }
78
79 *ret = object;
80
81 /* Try to create the initial size of the pool. An allocation failure at
82 * this time is not fatal..
83 */
84 for (unsigned int ii=0; ii < initial; ++ii)
85 if (grow_pool(ret) == -1)
86 break;
87 }
88
89 return ret;
90 }
91
92 memcached_st* memcached_pool_destroy(memcached_pool_st* pool)
93 {
94 memcached_st *ret = pool->master;
95
96 for (int xx= 0; xx <= pool->firstfree; ++xx)
97 {
98 memcached_free(pool->mmc[xx]);
99 free(pool->mmc[xx]);
100 pool->mmc[xx] = NULL;
101 }
102
103 pthread_mutex_destroy(&pool->mutex);
104 pthread_cond_destroy(&pool->cond);
105 free(pool->mmc);
106 free(pool);
107
108 return ret;
109 }
110
111 memcached_st* memcached_pool_pop(memcached_pool_st* pool,
112 bool block,
113 memcached_return *rc)
114 {
115 memcached_st *ret= NULL;
116 if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS)
117 return NULL;
118
119 do
120 {
121 if (pool->firstfree > -1)
122 ret= pool->mmc[pool->firstfree--];
123 else if (pool->current_size == pool->size)
124 {
125 if (!block)
126 {
127 *rc= mutex_exit(&pool->mutex);
128 return NULL;
129 }
130
131 if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1)
132 {
133 int err = errno;
134 mutex_exit(&pool->mutex);
135 errno = err;
136 *rc= MEMCACHED_ERRNO;
137 return NULL;
138 }
139 }
140 else if (grow_pool(pool) == -1)
141 {
142 *rc= mutex_exit(&pool->mutex);
143 return NULL;
144 }
145 }
146 while (ret == NULL);
147
148 *rc= mutex_exit(&pool->mutex);
149
150 return ret;
151 }
152
153 memcached_return memcached_pool_push(memcached_pool_st* pool,
154 memcached_st *mmc)
155 {
156 memcached_return rc= mutex_enter(&pool->mutex);
157
158 if (rc != MEMCACHED_SUCCESS)
159 return rc;
160
161 pool->mmc[++pool->firstfree]= mmc;
162
163 if (pool->firstfree == 0 && pool->current_size == pool->size)
164 {
165 /* we might have people waiting for a connection.. wake them up :-) */
166 pthread_cond_broadcast(&pool->cond);
167 }
168
169 return mutex_exit(&pool->mutex);
170 }