2 * Copyright (C) 2010 Brian Aker
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
8 * Summary: connects to a host, and makes sure it is alive.
12 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
13 #include "libmemcached/common.h"
14 #include "libmemcached/memcached_util.h"
19 struct memcached_pool_st
21 pthread_mutex_t mutex
;
27 uint32_t current_size
;
32 static memcached_return_t
mutex_enter(pthread_mutex_t
*mutex
)
36 ret
= pthread_mutex_lock(mutex
);
37 while (ret
== -1 && errno
== EINTR
);
39 return (ret
== -1) ? MEMCACHED_ERRNO
: MEMCACHED_SUCCESS
;
42 static memcached_return_t
mutex_exit(pthread_mutex_t
*mutex
)
46 ret
= pthread_mutex_unlock(mutex
);
47 while (ret
== -1 && errno
== EINTR
);
49 return (ret
== -1) ? MEMCACHED_ERRNO
: MEMCACHED_SUCCESS
;
53 * Grow the connection pool by creating a connection structure and clone the
54 * original memcached handle.
56 static int grow_pool(memcached_pool_st
* pool
)
58 memcached_st
*obj
= calloc(1, sizeof(*obj
));
63 if (memcached_clone(obj
, pool
->master
) == NULL
)
69 pool
->mmc
[++pool
->firstfree
] = obj
;
75 static inline memcached_pool_st
*_pool_create(memcached_st
* mmc
, uint32_t initial
, uint32_t max
)
77 memcached_pool_st
* ret
= NULL
;
79 if (! initial
|| ! max
|| initial
> max
)
85 memcached_pool_st object
= { .mutex
= PTHREAD_MUTEX_INITIALIZER
,
86 .cond
= PTHREAD_COND_INITIALIZER
,
88 .mmc
= calloc(max
, sizeof(memcached_st
*)),
92 ._owns_master
= false};
94 if (object
.mmc
!= NULL
)
96 ret
= (memcached_pool_st
*)calloc(1, sizeof(memcached_pool_st
));
100 errno
= ENOMEM
; // Set this for the failed calloc
107 Try to create the initial size of the pool. An allocation failure at
108 this time is not fatal..
110 for (unsigned int ii
= 0; ii
< initial
; ++ii
)
112 if (grow_pool(ret
) == -1)
120 memcached_pool_st
*memcached_pool_create(memcached_st
* mmc
, uint32_t initial
, uint32_t max
)
122 return _pool_create(mmc
, initial
, max
);
125 memcached_pool_st
* memcached_pool(const char *option_string
, size_t option_string_length
)
127 memcached_pool_st
*self
;
128 memcached_st
*memc
= memcached_create_with_options(option_string
, option_string_length
);
133 self
= memcached_pool_create(memc
, memc
->configure
.initial_pool_size
, memc
->configure
.max_pool_size
);
136 memcached_free(memc
);
142 self
->_owns_master
= true;
147 memcached_st
* memcached_pool_destroy(memcached_pool_st
* pool
)
152 memcached_st
*ret
= pool
->master
;
154 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
156 memcached_free(pool
->mmc
[xx
]);
158 pool
->mmc
[xx
] = NULL
;
161 pthread_mutex_destroy(&pool
->mutex
);
162 pthread_cond_destroy(&pool
->cond
);
164 if (pool
->_owns_master
)
166 memcached_free(pool
->master
);
174 memcached_st
* memcached_pool_pop(memcached_pool_st
* pool
,
176 memcached_return_t
*rc
)
184 memcached_st
*ret
= NULL
;
185 if ((*rc
= mutex_enter(&pool
->mutex
)) != MEMCACHED_SUCCESS
)
192 if (pool
->firstfree
> -1)
194 ret
= pool
->mmc
[pool
->firstfree
--];
196 else if (pool
->current_size
== pool
->size
)
200 *rc
= mutex_exit(&pool
->mutex
);
204 if (pthread_cond_wait(&pool
->cond
, &pool
->mutex
) == -1)
207 mutex_exit(&pool
->mutex
);
209 *rc
= MEMCACHED_ERRNO
;
213 else if (grow_pool(pool
) == -1)
215 *rc
= mutex_exit(&pool
->mutex
);
221 *rc
= mutex_exit(&pool
->mutex
);
226 memcached_return_t
memcached_pool_push(memcached_pool_st
* pool
,
230 return MEMCACHED_INVALID_ARGUMENTS
;
232 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
234 if (rc
!= MEMCACHED_SUCCESS
)
237 char* version
= memcached_get_user_data(mmc
);
238 /* Someone updated the behavior on the object.. */
239 if (version
!= pool
->version
)
242 memset(mmc
, 0, sizeof(*mmc
));
243 if (memcached_clone(mmc
, pool
->master
) == NULL
)
245 rc
= MEMCACHED_SOME_ERRORS
;
249 pool
->mmc
[++pool
->firstfree
]= mmc
;
251 if (pool
->firstfree
== 0 && pool
->current_size
== pool
->size
)
253 /* we might have people waiting for a connection.. wake them up :-) */
254 pthread_cond_broadcast(&pool
->cond
);
257 memcached_return_t rval
= mutex_exit(&pool
->mutex
);
258 if (rc
== MEMCACHED_SOME_ERRORS
)
265 memcached_return_t
memcached_pool_behavior_set(memcached_pool_st
*pool
,
266 memcached_behavior_t flag
,
270 return MEMCACHED_INVALID_ARGUMENTS
;
272 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
273 if (rc
!= MEMCACHED_SUCCESS
)
276 /* update the master */
277 rc
= memcached_behavior_set(pool
->master
, flag
, data
);
278 if (rc
!= MEMCACHED_SUCCESS
)
280 mutex_exit(&pool
->mutex
);
285 memcached_set_user_data(pool
->master
, pool
->version
);
286 /* update the clones */
287 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
289 rc
= memcached_behavior_set(pool
->mmc
[xx
], flag
, data
);
290 if (rc
== MEMCACHED_SUCCESS
)
292 memcached_set_user_data(pool
->mmc
[xx
], pool
->version
);
296 memcached_free(pool
->mmc
[xx
]);
297 memset(pool
->mmc
[xx
], 0, sizeof(*pool
->mmc
[xx
]));
299 if (memcached_clone(pool
->mmc
[xx
], pool
->master
) == NULL
)
301 /* I'm not sure what to do in this case.. this would happen
302 if we fail to push the server list inside the client..
303 I should add a testcase for this, but I believe the following
304 would work, except that you would add a hole in the pool list..
305 in theory you could end up with an empty pool....
313 return mutex_exit(&pool
->mutex
);
316 memcached_return_t
memcached_pool_behavior_get(memcached_pool_st
*pool
,
317 memcached_behavior_t flag
,
321 return MEMCACHED_INVALID_ARGUMENTS
;
323 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
324 if (rc
!= MEMCACHED_SUCCESS
)
329 *value
= memcached_behavior_get(pool
->master
, flag
);
331 return mutex_exit(&pool
->mutex
);