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
;
78 memcached_pool_st object
= { .mutex
= PTHREAD_MUTEX_INITIALIZER
,
79 .cond
= PTHREAD_COND_INITIALIZER
,
81 .mmc
= calloc(max
, sizeof(memcached_st
*)),
85 ._owns_master
= false};
87 if (object
.mmc
!= NULL
)
89 ret
= calloc(1, sizeof(*ret
));
99 Try to create the initial size of the pool. An allocation failure at
100 this time is not fatal..
102 for (unsigned int ii
= 0; ii
< initial
; ++ii
)
104 if (grow_pool(ret
) == -1)
112 memcached_pool_st
*memcached_pool_create(memcached_st
* mmc
, uint32_t initial
, uint32_t max
)
114 return _pool_create(mmc
, initial
, max
);
117 memcached_pool_st
* memcached_pool(const char *option_string
, size_t option_string_length
)
119 memcached_pool_st
*self
;
120 memcached_st
*memc
= memcached_create_with_options(option_string
, option_string_length
);
125 self
= memcached_pool_create(memc
, memc
->configure
.initial_pool_size
, memc
->configure
.max_pool_size
);
128 self
->_owns_master
= true;
132 memcached_free(memc
);
138 memcached_st
* memcached_pool_destroy(memcached_pool_st
* pool
)
143 memcached_st
*ret
= pool
->master
;
145 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
147 memcached_free(pool
->mmc
[xx
]);
149 pool
->mmc
[xx
] = NULL
;
152 pthread_mutex_destroy(&pool
->mutex
);
153 pthread_cond_destroy(&pool
->cond
);
155 if (pool
->_owns_master
)
157 memcached_free(pool
->master
);
165 memcached_st
* memcached_pool_pop(memcached_pool_st
* pool
,
167 memcached_return_t
*rc
)
169 memcached_st
*ret
= NULL
;
170 if ((*rc
= mutex_enter(&pool
->mutex
)) != MEMCACHED_SUCCESS
)
175 if (pool
->firstfree
> -1)
177 ret
= pool
->mmc
[pool
->firstfree
--];
179 else if (pool
->current_size
== pool
->size
)
183 *rc
= mutex_exit(&pool
->mutex
);
187 if (pthread_cond_wait(&pool
->cond
, &pool
->mutex
) == -1)
190 mutex_exit(&pool
->mutex
);
192 *rc
= MEMCACHED_ERRNO
;
196 else if (grow_pool(pool
) == -1)
198 *rc
= mutex_exit(&pool
->mutex
);
204 *rc
= mutex_exit(&pool
->mutex
);
209 memcached_return_t
memcached_pool_push(memcached_pool_st
* pool
,
212 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
214 if (rc
!= MEMCACHED_SUCCESS
)
217 char* version
= memcached_get_user_data(mmc
);
218 /* Someone updated the behavior on the object.. */
219 if (version
!= pool
->version
)
222 memset(mmc
, 0, sizeof(*mmc
));
223 if (memcached_clone(mmc
, pool
->master
) == NULL
)
225 rc
= MEMCACHED_SOME_ERRORS
;
229 pool
->mmc
[++pool
->firstfree
]= mmc
;
231 if (pool
->firstfree
== 0 && pool
->current_size
== pool
->size
)
233 /* we might have people waiting for a connection.. wake them up :-) */
234 pthread_cond_broadcast(&pool
->cond
);
237 memcached_return_t rval
= mutex_exit(&pool
->mutex
);
238 if (rc
== MEMCACHED_SOME_ERRORS
)
245 memcached_return_t
memcached_pool_behavior_set(memcached_pool_st
*pool
,
246 memcached_behavior_t flag
,
250 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
251 if (rc
!= MEMCACHED_SUCCESS
)
254 /* update the master */
255 rc
= memcached_behavior_set(pool
->master
, flag
, data
);
256 if (rc
!= MEMCACHED_SUCCESS
)
258 mutex_exit(&pool
->mutex
);
263 memcached_set_user_data(pool
->master
, pool
->version
);
264 /* update the clones */
265 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
267 rc
= memcached_behavior_set(pool
->mmc
[xx
], flag
, data
);
268 if (rc
== MEMCACHED_SUCCESS
)
270 memcached_set_user_data(pool
->mmc
[xx
], pool
->version
);
274 memcached_free(pool
->mmc
[xx
]);
275 memset(pool
->mmc
[xx
], 0, sizeof(*pool
->mmc
[xx
]));
277 if (memcached_clone(pool
->mmc
[xx
], pool
->master
) == NULL
)
279 /* I'm not sure what to do in this case.. this would happen
280 if we fail to push the server list inside the client..
281 I should add a testcase for this, but I believe the following
282 would work, except that you would add a hole in the pool list..
283 in theory you could end up with an empty pool....
291 return mutex_exit(&pool
->mutex
);
294 memcached_return_t
memcached_pool_behavior_get(memcached_pool_st
*pool
,
295 memcached_behavior_t flag
,
298 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
300 if (rc
!= MEMCACHED_SUCCESS
)
305 *value
= memcached_behavior_get(pool
->master
, flag
);
307 return mutex_exit(&pool
->mutex
);