1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2010 Brian Aker All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 #include <libmemcachedutil/common.h>
46 struct memcached_pool_st
48 pthread_mutex_t mutex
;
51 memcached_st
**server_pool
;
54 uint32_t current_size
;
56 struct timespec _timeout
;
58 memcached_pool_st(memcached_st
*master_arg
, size_t max_arg
) :
62 size(uint32_t(max_arg
)),
66 pthread_mutex_init(&mutex
, NULL
);
67 pthread_cond_init(&cond
, NULL
);
72 const struct timespec
& timeout() const
77 bool release(memcached_st
*, memcached_return_t
& rc
);
79 memcached_st
*fetch(memcached_return_t
& rc
);
80 memcached_st
*fetch(const struct timespec
&, memcached_return_t
& rc
);
82 bool init(uint32_t initial
);
86 for (int x
= 0; x
<= firstfree
; ++x
)
88 memcached_free(server_pool
[x
]);
93 if ((error
= pthread_mutex_destroy(&mutex
)) != 0)
95 assert_vmsg(error
!= 0, "pthread_mutex_destroy() %s(%d)", strerror(error
), error
);
98 if ((error
= pthread_cond_destroy(&cond
)) != 0)
100 assert_vmsg(error
!= 0, "pthread_cond_destroy() %s", strerror(error
));
103 delete [] server_pool
;
106 memcached_free(master
);
110 void increment_version()
112 ++master
->configure
.version
;
115 bool compare_version(const memcached_st
*arg
) const
117 return (arg
->configure
.version
== version());
120 int32_t version() const
122 return master
->configure
.version
;
128 * Grow the connection pool by creating a connection structure and clone the
129 * original memcached handle.
131 static bool grow_pool(memcached_pool_st
* pool
)
136 if (not (obj
= memcached_clone(NULL
, pool
->master
)))
141 pool
->server_pool
[++pool
->firstfree
]= obj
;
142 pool
->current_size
++;
143 obj
->configure
.version
= pool
->version();
148 bool memcached_pool_st::init(uint32_t initial
)
150 server_pool
= new (std::nothrow
) memcached_st
*[size
];
151 if (server_pool
== NULL
)
157 Try to create the initial size of the pool. An allocation failure at
158 this time is not fatal..
160 for (unsigned int x
= 0; x
< initial
; ++x
)
162 if (grow_pool(this) == false)
172 static inline memcached_pool_st
*_pool_create(memcached_st
* master
, uint32_t initial
, uint32_t max
)
174 if (initial
== 0 or max
== 0 or (initial
> max
))
179 memcached_pool_st
*object
= new (std::nothrow
) memcached_pool_st(master
, max
);
186 Try to create the initial size of the pool. An allocation failure at
187 this time is not fatal..
189 if (not object
->init(initial
))
198 memcached_pool_st
*memcached_pool_create(memcached_st
* master
, uint32_t initial
, uint32_t max
)
200 return _pool_create(master
, initial
, max
);
203 memcached_pool_st
* memcached_pool(const char *option_string
, size_t option_string_length
)
205 memcached_st
*memc
= memcached(option_string
, option_string_length
);
212 memcached_pool_st
*self
= memcached_pool_create(memc
, memc
->configure
.initial_pool_size
, memc
->configure
.max_pool_size
);
215 memcached_free(memc
);
219 self
->_owns_master
= true;
224 memcached_st
* memcached_pool_destroy(memcached_pool_st
* pool
)
231 // Legacy that we return the original structure
232 memcached_st
*ret
= NULL
;
233 if (pool
->_owns_master
)
245 memcached_st
* memcached_pool_st::fetch(memcached_return_t
& rc
)
247 static struct timespec relative_time
= { 0, 0 };
248 return fetch(relative_time
, rc
);
251 memcached_st
* memcached_pool_st::fetch(const struct timespec
& relative_time
, memcached_return_t
& rc
)
253 rc
= MEMCACHED_SUCCESS
;
256 if ((error
= pthread_mutex_lock(&mutex
)) != 0)
258 rc
= MEMCACHED_IN_PROGRESS
;
262 memcached_st
*ret
= NULL
;
267 ret
= server_pool
[firstfree
--];
269 else if (current_size
== size
)
271 if (relative_time
.tv_sec
== 0 and relative_time
.tv_nsec
== 0)
273 error
= pthread_mutex_unlock(&mutex
);
274 rc
= MEMCACHED_NOTFOUND
;
279 struct timespec time_to_wait
= {0, 0};
280 time_to_wait
.tv_sec
= time(NULL
) +relative_time
.tv_sec
;
281 time_to_wait
.tv_nsec
= relative_time
.tv_nsec
;
284 if ((thread_ret
= pthread_cond_timedwait(&cond
, &mutex
, &time_to_wait
)) != 0)
287 if ((unlock_error
= pthread_mutex_unlock(&mutex
)) != 0)
289 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
292 if (thread_ret
== ETIMEDOUT
)
294 rc
= MEMCACHED_TIMEOUT
;
305 else if (grow_pool(this) == false)
308 if ((unlock_error
= pthread_mutex_unlock(&mutex
)) != 0)
310 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
315 } while (ret
== NULL
);
317 if ((error
= pthread_mutex_unlock(&mutex
)) != 0)
319 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
325 bool memcached_pool_st::release(memcached_st
*released
, memcached_return_t
& rc
)
327 rc
= MEMCACHED_SUCCESS
;
328 if (released
== NULL
)
330 rc
= MEMCACHED_INVALID_ARGUMENTS
;
335 if ((error
= pthread_mutex_lock(&mutex
)))
337 rc
= MEMCACHED_IN_PROGRESS
;
342 Someone updated the behavior on the object, so we clone a new memcached_st with the new settings. If we fail to clone, we keep the old one around.
344 if (compare_version(released
) == false)
347 if ((memc
= memcached_clone(NULL
, master
)))
349 memcached_free(released
);
354 server_pool
[++firstfree
]= released
;
356 if (firstfree
== 0 and current_size
== size
)
358 /* we might have people waiting for a connection.. wake them up :-) */
359 if ((error
= pthread_cond_broadcast(&cond
)) != 0)
361 assert_vmsg(error
!= 0, "pthread_cond_broadcast() %s", strerror(error
));
365 if ((error
= pthread_mutex_unlock(&mutex
)) != 0)
372 memcached_st
* memcached_pool_fetch(memcached_pool_st
* pool
, struct timespec
* relative_time
, memcached_return_t
* rc
)
379 memcached_return_t unused
;
385 if (relative_time
== NULL
)
387 return pool
->fetch(*rc
);
390 return pool
->fetch(*relative_time
, *rc
);
393 memcached_st
* memcached_pool_pop(memcached_pool_st
* pool
,
395 memcached_return_t
*rc
)
402 memcached_return_t unused
;
411 memc
= pool
->fetch(pool
->timeout(), *rc
);
415 memc
= pool
->fetch(*rc
);
421 memcached_return_t
memcached_pool_release(memcached_pool_st
* pool
, memcached_st
*released
)
425 return MEMCACHED_INVALID_ARGUMENTS
;
428 memcached_return_t rc
;
430 (void) pool
->release(released
, rc
);
435 memcached_return_t
memcached_pool_push(memcached_pool_st
* pool
, memcached_st
*released
)
437 return memcached_pool_release(pool
, released
);
441 memcached_return_t
memcached_pool_behavior_set(memcached_pool_st
*pool
,
442 memcached_behavior_t flag
,
447 return MEMCACHED_INVALID_ARGUMENTS
;
451 if ((error
= pthread_mutex_lock(&pool
->mutex
)))
453 return MEMCACHED_IN_PROGRESS
;
456 /* update the master */
457 memcached_return_t rc
= memcached_behavior_set(pool
->master
, flag
, data
);
458 if (memcached_failed(rc
))
460 if ((error
= pthread_mutex_unlock(&pool
->mutex
)) != 0)
462 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
467 pool
->increment_version();
468 /* update the clones */
469 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
471 if (memcached_success(memcached_behavior_set(pool
->server_pool
[xx
], flag
, data
)))
473 pool
->server_pool
[xx
]->configure
.version
= pool
->version();
478 if ((memc
= memcached_clone(NULL
, pool
->master
)))
480 memcached_free(pool
->server_pool
[xx
]);
481 pool
->server_pool
[xx
]= memc
;
482 /* I'm not sure what to do in this case.. this would happen
483 if we fail to push the server list inside the client..
484 I should add a testcase for this, but I believe the following
485 would work, except that you would add a hole in the pool list..
486 in theory you could end up with an empty pool....
492 if ((error
= pthread_mutex_unlock(&pool
->mutex
)) != 0)
494 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
500 memcached_return_t
memcached_pool_behavior_get(memcached_pool_st
*pool
,
501 memcached_behavior_t flag
,
506 return MEMCACHED_INVALID_ARGUMENTS
;
510 if ((error
= pthread_mutex_lock(&pool
->mutex
)))
512 return MEMCACHED_IN_PROGRESS
;
515 *value
= memcached_behavior_get(pool
->master
, flag
);
517 if ((error
= pthread_mutex_unlock(&pool
->mutex
)) != 0)
519 assert_vmsg(error
!= 0, "pthread_mutex_unlock() %s", strerror(error
));
522 return MEMCACHED_SUCCESS
;