002dac82e40f90fb7dae9316608ebf76bf32d03b
[awesomized/libmemcached] / pool.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached Client and Server
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
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
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
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.
35 *
36 */
37
38 #include <config.h>
39 #include <libtest/test.hpp>
40
41 using namespace libtest;
42
43 #include <vector>
44 #include <iostream>
45 #include <string>
46 #include <cerrno>
47
48 #include <semaphore.h>
49
50 #include <libmemcached/memcached.h>
51 #include <libmemcached/util.h>
52 #include <tests/pool.h>
53
54 test_return_t memcached_pool_test(memcached_st *)
55 {
56 memcached_return_t rc;
57 const char *config_string= "--SERVER=host10.example.com --SERVER=host11.example.com --SERVER=host10.example.com --POOL-MIN=10 --POOL-MAX=32";
58
59 char buffer[2048];
60 rc= libmemcached_check_configuration(config_string, sizeof(config_string) -1, buffer, sizeof(buffer));
61
62 test_true_got(rc != MEMCACHED_SUCCESS, buffer);
63
64 memcached_pool_st* pool= memcached_pool(config_string, strlen(config_string));
65 test_true_got(pool, strerror(errno));
66
67 memcached_st *memc= memcached_pool_pop(pool, false, &rc);
68
69 test_true(rc == MEMCACHED_SUCCESS);
70 test_true(memc);
71
72 /*
73 Release the memc_ptr that was pulled from the pool
74 */
75 memcached_pool_push(pool, memc);
76
77 /*
78 Destroy the pool.
79 */
80 memcached_pool_destroy(pool);
81
82 return TEST_SUCCESS;
83 }
84
85
86 #define POOL_SIZE 10
87 test_return_t connection_pool_test(memcached_st *memc)
88 {
89 memcached_pool_st* pool= memcached_pool_create(memc, 5, POOL_SIZE);
90 test_true(pool);
91 memcached_st *mmc[POOL_SIZE];
92
93 // Fill up our array that we will store the memc that are in the pool
94 for (size_t x= 0; x < POOL_SIZE; ++x)
95 {
96 memcached_return_t rc;
97 mmc[x]= memcached_pool_fetch(pool, NULL, &rc);
98 test_compare(MEMCACHED_SUCCESS, rc);
99 test_true(mmc[x]);
100 }
101
102 // All memc should be gone
103 {
104 memcached_return_t rc;
105 test_null(memcached_pool_fetch(pool, NULL, &rc));
106 test_compare(MEMCACHED_NOTFOUND, rc);
107 }
108
109 // Release them..
110 for (size_t x= 0; x < POOL_SIZE; ++x)
111 {
112 if (mmc[x])
113 {
114 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[x]));
115 }
116 }
117 test_true(memcached_pool_destroy(pool) == memc);
118
119 return TEST_SUCCESS;
120 }
121
122 test_return_t connection_pool2_test(memcached_st *memc)
123 {
124 memcached_pool_st* pool= memcached_pool_create(memc, 5, POOL_SIZE);
125 test_true(pool);
126 memcached_st *mmc[POOL_SIZE];
127
128 // Fill up our array that we will store the memc that are in the pool
129 for (size_t x= 0; x < POOL_SIZE; ++x)
130 {
131 memcached_return_t rc;
132 mmc[x]= memcached_pool_fetch(pool, NULL, &rc);
133 test_compare(MEMCACHED_SUCCESS, rc);
134 test_true(mmc[x]);
135 }
136
137 // All memc should be gone
138 {
139 memcached_return_t rc;
140 test_null(memcached_pool_fetch(pool, NULL, &rc));
141 test_compare(MEMCACHED_NOTFOUND, rc);
142 }
143
144 // verify that I can do ops with all connections
145 test_compare(MEMCACHED_SUCCESS,
146 memcached_set(mmc[0],
147 test_literal_param("key"),
148 "0", 1, 0, 0));
149
150 for (uint64_t x= 0; x < POOL_SIZE; ++x)
151 {
152 uint64_t number_value;
153 test_compare(MEMCACHED_SUCCESS,
154 memcached_increment(mmc[x],
155 test_literal_param("key"),
156 1, &number_value));
157 test_compare(number_value, (x+1));
158 }
159
160 // Release them..
161 for (size_t x= 0; x < POOL_SIZE; ++x)
162 {
163 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[x]));
164 }
165
166
167 /* verify that I can set behaviors on the pool when I don't have all
168 * of the connections in the pool. It should however be enabled
169 * when I push the item into the pool
170 */
171 mmc[0]= memcached_pool_fetch(pool, NULL, NULL);
172 test_true(mmc[0]);
173
174 test_compare(MEMCACHED_SUCCESS,
175 memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, 9999));
176
177 {
178 memcached_return_t rc;
179 mmc[1]= memcached_pool_fetch(pool, NULL, &rc);
180 test_true(mmc[1]);
181 test_compare(MEMCACHED_SUCCESS, rc);
182 }
183
184 test_compare(UINT64_C(9999), memcached_behavior_get(mmc[1], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK));
185 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[1]));
186 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[0]));
187
188 {
189 memcached_return_t rc;
190 mmc[0]= memcached_pool_fetch(pool, NULL, &rc);
191 test_true(mmc[0]);
192 test_compare(MEMCACHED_SUCCESS, rc);
193 }
194
195 test_compare(UINT64_C(9999), memcached_behavior_get(mmc[0], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK));
196 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[0]));
197
198 test_true(memcached_pool_destroy(pool) == memc);
199
200 return TEST_SUCCESS;
201 }
202
203 struct test_pool_context_st {
204 volatile memcached_return_t rc;
205 memcached_pool_st* pool;
206 memcached_st* mmc;
207 sem_t _lock;
208
209 test_pool_context_st(memcached_pool_st *pool_arg, memcached_st *memc_arg):
210 rc(MEMCACHED_FAILURE),
211 pool(pool_arg),
212 mmc(memc_arg)
213 {
214 sem_init(&_lock, 0, 0);
215 }
216
217 void wait()
218 {
219 sem_wait(&_lock);
220 }
221
222 void release()
223 {
224 sem_post(&_lock);
225 }
226
227 ~test_pool_context_st()
228 {
229 sem_destroy(&_lock);
230 }
231 };
232
233 static void* connection_release(void *arg)
234 {
235 test_pool_context_st *resource= static_cast<test_pool_context_st *>(arg);
236 assert(resource);
237 if (resource == NULL)
238 {
239 abort();
240 }
241
242 // Release all of the memc we are holding
243 resource->rc= memcached_pool_release(resource->pool, resource->mmc);
244 resource->release();
245
246 pthread_exit(arg);
247 }
248
249 test_return_t connection_pool3_test(memcached_st *memc)
250 {
251 #ifdef __APPLE__
252 return TEST_SKIPPED;
253 #endif
254
255 memcached_pool_st* pool= memcached_pool_create(memc, 1, 1);
256 test_true(pool);
257
258 memcached_st *pool_memc;
259 {
260 memcached_return_t rc;
261 pool_memc= memcached_pool_fetch(pool, NULL, &rc);
262 test_compare(MEMCACHED_SUCCESS, rc);
263 test_true(pool_memc);
264 }
265
266 /*
267 @note This comment was written to describe what was believed to be the original authors intent.
268
269 This portion of the test creates a thread that will wait until told to free a memcached_st
270 that will be grabbed by the main thread.
271
272 It is believed that this tests whether or not we are handling ownership correctly.
273 */
274 pthread_t tid;
275 test_pool_context_st item(pool, pool_memc);
276
277 test_zero(pthread_create(&tid, NULL, connection_release, &item));
278 item.wait();
279
280 memcached_return_t rc;
281 memcached_st *pop_memc;
282 // We do a hard loop, and try N times
283 int counter= 5;
284 do
285 {
286 struct timespec relative_time= { 0, 0 };
287 pop_memc= memcached_pool_fetch(pool, &relative_time, &rc);
288
289 if (memcached_success(rc))
290 {
291 break;
292 }
293
294 if (memcached_failed(rc))
295 {
296 test_null(pop_memc);
297 test_true(rc != MEMCACHED_TIMEOUT); // As long as relative_time is zero, MEMCACHED_TIMEOUT is invalid
298 }
299 } while (--counter);
300
301 if (memcached_failed(rc)) // Cleanup thread since we will exit once we test.
302 {
303 pthread_join(tid, NULL);
304 test_compare(MEMCACHED_SUCCESS, rc);
305 }
306
307 {
308 int pthread_ret= pthread_join(tid, NULL);
309 test_true(pthread_ret == 0 or pthread_ret == ESRCH);
310 }
311 test_compare(MEMCACHED_SUCCESS, rc);
312 test_true(pool_memc == pop_memc);
313
314 test_true(memcached_pool_destroy(pool) == memc);
315
316 return TEST_SUCCESS;
317 }