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 <libmemcached/common.h>
40 #include <libmemcached/memcached_util.h>
47 static bool grow_pool(memcached_pool_st
* pool
);
49 struct memcached_pool_st
51 pthread_mutex_t mutex
;
54 memcached_st
**server_pool
;
57 uint32_t current_size
;
60 memcached_pool_st(memcached_st
*master_arg
, size_t max_arg
) :
68 pthread_mutex_init(&mutex
, NULL
);
69 pthread_cond_init(&cond
, NULL
);
72 bool init(uint32_t initial
)
74 server_pool
= new (std::nothrow
) memcached_st
*[size
];
79 Try to create the initial size of the pool. An allocation failure at
80 this time is not fatal..
82 for (unsigned int x
= 0; x
< initial
; ++x
)
84 if (not grow_pool(this))
93 for (int x
= 0; x
<= firstfree
; ++x
)
95 memcached_free(server_pool
[x
]);
96 server_pool
[x
] = NULL
;
99 pthread_mutex_destroy(&mutex
);
100 pthread_cond_destroy(&cond
);
101 delete [] server_pool
;
104 memcached_free(master
);
108 void increment_version()
110 ++master
->configure
.version
;
113 bool compare_version(const memcached_st
*arg
) const
115 return (arg
->configure
.version
== version());
118 int32_t version() const
120 return master
->configure
.version
;
124 static memcached_return_t
mutex_enter(pthread_mutex_t
*mutex
)
129 ret
= pthread_mutex_lock(mutex
);
130 } while (ret
== -1 && errno
== EINTR
);
132 return (ret
== -1) ? MEMCACHED_ERRNO
: MEMCACHED_SUCCESS
;
135 static memcached_return_t
mutex_exit(pthread_mutex_t
*mutex
)
140 ret
= pthread_mutex_unlock(mutex
);
141 } while (ret
== -1 && errno
== EINTR
);
143 return (ret
== -1) ? MEMCACHED_ERRNO
: MEMCACHED_SUCCESS
;
147 * Grow the connection pool by creating a connection structure and clone the
148 * original memcached handle.
150 static bool grow_pool(memcached_pool_st
* pool
)
153 if (not (obj
= memcached_clone(NULL
, pool
->master
)))
158 pool
->server_pool
[++pool
->firstfree
]= obj
;
159 pool
->current_size
++;
160 obj
->configure
.version
= pool
->version();
165 static inline memcached_pool_st
*_pool_create(memcached_st
* master
, uint32_t initial
, uint32_t max
)
167 if (initial
== 0 or max
== 0 or (initial
> max
))
173 memcached_pool_st
*object
= new (std::nothrow
) memcached_pool_st(master
, max
);
176 errno
= ENOMEM
; // Set this for the failed calloc
181 Try to create the initial size of the pool. An allocation failure at
182 this time is not fatal..
184 if (not object
->init(initial
))
193 memcached_pool_st
*memcached_pool_create(memcached_st
* master
, uint32_t initial
, uint32_t max
)
195 return _pool_create(master
, initial
, max
);
198 memcached_pool_st
* memcached_pool(const char *option_string
, size_t option_string_length
)
200 memcached_st
*memc
= memcached(option_string
, option_string_length
);
207 memcached_pool_st
*self
= memcached_pool_create(memc
, memc
->configure
.initial_pool_size
, memc
->configure
.max_pool_size
);
210 memcached_free(memc
);
216 self
->_owns_master
= true;
221 memcached_st
* memcached_pool_destroy(memcached_pool_st
* pool
)
228 // Legacy that we return the original structure
229 memcached_st
*ret
= NULL
;
230 if (pool
->_owns_master
)
242 memcached_st
* memcached_pool_pop(memcached_pool_st
* pool
,
244 memcached_return_t
*rc
)
246 memcached_return_t not_used
;
254 *rc
= MEMCACHED_INVALID_ARGUMENTS
;
260 if (memcached_failed((*rc
= mutex_enter(&pool
->mutex
))))
265 memcached_st
*ret
= NULL
;
268 if (pool
->firstfree
> -1)
270 ret
= pool
->server_pool
[pool
->firstfree
--];
272 else if (pool
->current_size
== pool
->size
)
276 *rc
= mutex_exit(&pool
->mutex
); // this should be a different error
280 struct timespec time_to_wait
= {0, 0};
281 time_to_wait
.tv_sec
= time(NULL
) +5;
283 if ((thread_ret
= pthread_cond_timedwait(&pool
->cond
, &pool
->mutex
, &time_to_wait
)) != 0)
285 mutex_exit(&pool
->mutex
);
288 if (thread_ret
== ETIMEDOUT
)
290 *rc
= MEMCACHED_TIMEOUT
;
294 *rc
= MEMCACHED_ERRNO
;
300 else if (not grow_pool(pool
))
302 (void)mutex_exit(&pool
->mutex
);
303 *rc
= MEMCACHED_MEMORY_ALLOCATION_FAILURE
;
306 } while (ret
== NULL
);
308 *rc
= mutex_exit(&pool
->mutex
);
313 memcached_return_t
memcached_pool_push(memcached_pool_st
* pool
, memcached_st
*released
)
317 return MEMCACHED_INVALID_ARGUMENTS
;
320 if (released
== NULL
)
322 return MEMCACHED_INVALID_ARGUMENTS
;
325 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
327 if (rc
!= MEMCACHED_SUCCESS
)
332 /* Someone updated the behavior on the object.. */
333 if (pool
->compare_version(released
) == false)
335 memcached_free(released
);
336 if (not (released
= memcached_clone(NULL
, pool
->master
)))
338 rc
= MEMCACHED_SOME_ERRORS
;
342 pool
->server_pool
[++pool
->firstfree
]= released
;
344 if (pool
->firstfree
== 0 and pool
->current_size
== pool
->size
)
346 /* we might have people waiting for a connection.. wake them up :-) */
347 pthread_cond_broadcast(&pool
->cond
);
350 memcached_return_t rval
= mutex_exit(&pool
->mutex
);
351 if (rc
== MEMCACHED_SOME_ERRORS
)
360 memcached_return_t
memcached_pool_behavior_set(memcached_pool_st
*pool
,
361 memcached_behavior_t flag
,
366 return MEMCACHED_INVALID_ARGUMENTS
;
369 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
370 if (rc
!= MEMCACHED_SUCCESS
)
373 /* update the master */
374 rc
= memcached_behavior_set(pool
->master
, flag
, data
);
375 if (rc
!= MEMCACHED_SUCCESS
)
377 mutex_exit(&pool
->mutex
);
381 pool
->increment_version();
382 /* update the clones */
383 for (int xx
= 0; xx
<= pool
->firstfree
; ++xx
)
385 rc
= memcached_behavior_set(pool
->server_pool
[xx
], flag
, data
);
386 if (rc
== MEMCACHED_SUCCESS
)
388 pool
->server_pool
[xx
]->configure
.version
= pool
->version();
392 memcached_free(pool
->server_pool
[xx
]);
393 if (not (pool
->server_pool
[xx
]= memcached_clone(NULL
, pool
->master
)))
395 /* I'm not sure what to do in this case.. this would happen
396 if we fail to push the server list inside the client..
397 I should add a testcase for this, but I believe the following
398 would work, except that you would add a hole in the pool list..
399 in theory you could end up with an empty pool....
405 return mutex_exit(&pool
->mutex
);
408 memcached_return_t
memcached_pool_behavior_get(memcached_pool_st
*pool
,
409 memcached_behavior_t flag
,
414 return MEMCACHED_INVALID_ARGUMENTS
;
417 memcached_return_t rc
= mutex_enter(&pool
->mutex
);
418 if (rc
!= MEMCACHED_SUCCESS
)
423 *value
= memcached_behavior_get(pool
->master
, flag
);
425 return mutex_exit(&pool
->mutex
);