~piotr-sikora/libmemcached/fix-tests-on-openbsd

« back to all changes in this revision

Viewing changes to libmemcached/util/pool.cc

  • Committer: Brian Aker
  • Date: 2011-04-28 00:30:03 UTC
  • mfrom: (929.1.88 libmemcached-build2)
  • Revision ID: brian@tangent.org-20110428003003-bm1qzgmg9kaawp5d
Merge in code for C++ compiling of libmemcached.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* LibMemcached
2
 
 * Copyright (C) 2010 Brian Aker
3
 
 * All rights reserved.
4
 
 *
5
 
 * Use and distribution licensed under the BSD license.  See
6
 
 * the COPYING file in the parent directory for full text.
7
 
 *
8
 
 * Summary: connects to a host, and makes sure it is alive.
 
1
/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
2
 * 
 
3
 *  Libmemcached library
 
4
 *
 
5
 *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
 
6
 *  Copyright (C) 2010 Brian Aker 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.
9
35
 *
10
36
 */
11
37
 
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"
15
 
 
16
 
#include <errno.h>
 
38
 
 
39
#include <libmemcached/common.h>
 
40
#include <libmemcached/memcached_util.h>
 
41
 
 
42
#include <cassert>
 
43
#include <cerrno>
17
44
#include <pthread.h>
 
45
#include <memory>
 
46
 
 
47
static bool grow_pool(memcached_pool_st* pool);
18
48
 
19
49
struct memcached_pool_st
20
50
{
21
51
  pthread_mutex_t mutex;
22
52
  pthread_cond_t cond;
23
53
  memcached_st *master;
24
 
  memcached_st **mmc;
 
54
  memcached_st **server_pool;
25
55
  int firstfree;
26
 
  uint32_t size;
 
56
  const uint32_t size;
27
57
  uint32_t current_size;
28
58
  bool _owns_master;
29
 
  char *version;
 
59
 
 
60
  memcached_pool_st(memcached_st *master_arg, size_t max_arg) :
 
61
    master(master_arg),
 
62
    server_pool(NULL),
 
63
    firstfree(-1),
 
64
    size(max_arg),
 
65
    current_size(0),
 
66
    _owns_master(false)
 
67
  {
 
68
    pthread_mutex_init(&mutex, NULL);
 
69
    pthread_cond_init(&cond, NULL);
 
70
  }
 
71
 
 
72
  bool init(uint32_t initial)
 
73
  {
 
74
    server_pool= new (std::nothrow) memcached_st *[size];
 
75
    if (not server_pool)
 
76
      return false;
 
77
 
 
78
    /*
 
79
      Try to create the initial size of the pool. An allocation failure at
 
80
      this time is not fatal..
 
81
    */
 
82
    for (unsigned int x= 0; x < initial; ++x)
 
83
    {
 
84
      if (not grow_pool(this))
 
85
        break;
 
86
    }
 
87
 
 
88
    return true;
 
89
  }
 
90
 
 
91
  ~memcached_pool_st()
 
92
  {
 
93
    for (int x= 0; x <= firstfree; ++x)
 
94
    {
 
95
      memcached_free(server_pool[x]);
 
96
      server_pool[x] = NULL;
 
97
    }
 
98
 
 
99
    pthread_mutex_destroy(&mutex);
 
100
    pthread_cond_destroy(&cond);
 
101
    delete [] server_pool;
 
102
    if (_owns_master)
 
103
    {
 
104
      memcached_free(master);
 
105
    }
 
106
  }
 
107
 
 
108
  void increment_version()
 
109
  {
 
110
    ++master->configure.version;
 
111
  }
 
112
 
 
113
  bool compare_version(const memcached_st *arg) const
 
114
  {
 
115
    return (arg->configure.version == version());
 
116
  }
 
117
 
 
118
  int32_t version() const
 
119
  {
 
120
    return master->configure.version;
 
121
  }
30
122
};
31
123
 
32
124
static memcached_return_t mutex_enter(pthread_mutex_t *mutex)
33
125
{
34
126
  int ret;
35
127
  do
 
128
  {
36
129
    ret= pthread_mutex_lock(mutex);
37
 
  while (ret == -1 && errno == EINTR);
 
130
  } while (ret == -1 && errno == EINTR);
38
131
 
39
132
  return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS;
40
133
}
43
136
{
44
137
  int ret;
45
138
  do
 
139
  {
46
140
    ret= pthread_mutex_unlock(mutex);
47
 
  while (ret == -1 && errno == EINTR);
 
141
  } while (ret == -1 && errno == EINTR);
48
142
 
49
143
  return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS;
50
144
}
53
147
 * Grow the connection pool by creating a connection structure and clone the
54
148
 * original memcached handle.
55
149
 */
56
 
static int grow_pool(memcached_pool_st* pool)
 
150
static bool grow_pool(memcached_pool_st* pool)
57
151
{
58
 
  memcached_st *obj= calloc(1, sizeof(*obj));
59
 
 
60
 
  if (obj == NULL)
61
 
    return -1;
62
 
 
63
 
  if (memcached_clone(obj, pool->master) == NULL)
 
152
  memcached_st *obj;
 
153
  if (not (obj= memcached_clone(NULL, pool->master)))
64
154
  {
65
 
    free(obj);
66
 
    return -1;
 
155
    return false;
67
156
  }
68
157
 
69
 
  pool->mmc[++pool->firstfree] = obj;
 
158
  pool->server_pool[++pool->firstfree]= obj;
70
159
  pool->current_size++;
 
160
  obj->configure.version= pool->version();
71
161
 
72
 
  return EXIT_SUCCESS;
 
162
  return true;
73
163
}
74
164
 
75
 
static inline memcached_pool_st *_pool_create(memcached_st* mmc, uint32_t initial, uint32_t max)
 
165
static inline memcached_pool_st *_pool_create(memcached_st* master, uint32_t initial, uint32_t max)
76
166
{
77
 
  memcached_pool_st* ret= NULL;
78
 
 
79
167
  if (! initial || ! max || initial > max)
80
168
  {
81
169
    errno= EINVAL;
82
170
    return NULL;
83
171
  }
84
172
 
85
 
  memcached_pool_st object= { .mutex = PTHREAD_MUTEX_INITIALIZER,
86
 
    .cond= PTHREAD_COND_INITIALIZER,
87
 
    .master= mmc,
88
 
    .mmc= calloc(max, sizeof(memcached_st*)),
89
 
    .firstfree= -1,
90
 
    .size= max,
91
 
    .current_size= 0,
92
 
    ._owns_master= false};
93
 
 
94
 
  if (object.mmc != NULL)
95
 
  {
96
 
    ret= (memcached_pool_st*)calloc(1, sizeof(memcached_pool_st));
97
 
    if (ret == NULL)
98
 
    {
99
 
      free(object.mmc);
100
 
      errno= ENOMEM; // Set this for the failed calloc
101
 
      return NULL;
102
 
    }
103
 
 
104
 
    *ret= object;
105
 
 
106
 
    /*
107
 
      Try to create the initial size of the pool. An allocation failure at
108
 
      this time is not fatal..
109
 
    */
110
 
    for (unsigned int ii= 0; ii < initial; ++ii)
111
 
    {
112
 
      if (grow_pool(ret) == -1)
113
 
        break;
114
 
    }
115
 
  }
116
 
 
117
 
  return ret;
 
173
  memcached_pool_st *object= new (std::nothrow) memcached_pool_st(master, max);
 
174
  if (not object)
 
175
  {
 
176
    errno= ENOMEM; // Set this for the failed calloc
 
177
    return NULL;
 
178
  }
 
179
 
 
180
  /*
 
181
    Try to create the initial size of the pool. An allocation failure at
 
182
    this time is not fatal..
 
183
  */
 
184
  if (not object->init(initial))
 
185
  {
 
186
    delete object;
 
187
    return NULL;
 
188
  }
 
189
 
 
190
  return object;
118
191
}
119
192
 
120
 
memcached_pool_st *memcached_pool_create(memcached_st* mmc, uint32_t initial, uint32_t max)
 
193
memcached_pool_st *memcached_pool_create(memcached_st* master, uint32_t initial, uint32_t max)
121
194
{
122
 
  return _pool_create(mmc, initial, max);
 
195
  return _pool_create(master, initial, max);
123
196
}
124
197
 
125
198
memcached_pool_st * memcached_pool(const char *option_string, size_t option_string_length)
126
199
{
 
200
  memcached_st *memc= memcached(option_string, option_string_length);
 
201
 
 
202
  if (not memc)
 
203
    return NULL;
 
204
 
127
205
  memcached_pool_st *self;
128
 
  memcached_st *memc= memcached(option_string, option_string_length);
129
 
 
130
 
  if (! memc)
131
 
    return NULL;
132
 
 
133
206
  self= memcached_pool_create(memc, memc->configure.initial_pool_size, memc->configure.max_pool_size);
134
 
  if (! self)
 
207
  if (not self)
135
208
  {
136
209
    memcached_free(memc);
137
210
    errno= ENOMEM;
146
219
 
147
220
memcached_st*  memcached_pool_destroy(memcached_pool_st* pool)
148
221
{
149
 
  if (! pool)
 
222
  if (not pool)
150
223
    return NULL;
151
224
 
152
 
  memcached_st *ret= pool->master;
153
 
 
154
 
  for (int xx= 0; xx <= pool->firstfree; ++xx)
155
 
  {
156
 
    memcached_free(pool->mmc[xx]);
157
 
    free(pool->mmc[xx]);
158
 
    pool->mmc[xx] = NULL;
159
 
  }
160
 
 
161
 
  pthread_mutex_destroy(&pool->mutex);
162
 
  pthread_cond_destroy(&pool->cond);
163
 
  free(pool->mmc);
 
225
  // Legacy that we return the original structure
 
226
  memcached_st *ret= NULL;
164
227
  if (pool->_owns_master)
 
228
  { }
 
229
  else
165
230
  {
166
 
    memcached_free(pool->master);
167
 
    ret= NULL;
 
231
    ret= pool->master;
168
232
  }
169
 
  free(pool);
 
233
 
 
234
  delete pool;
170
235
 
171
236
  return ret;
172
237
}
175
240
                                 bool block,
176
241
                                 memcached_return_t *rc)
177
242
{
178
 
  if (! pool || ! rc)
 
243
  assert(pool);
 
244
  assert(rc);
 
245
  if (not pool ||  not rc)
179
246
  {
180
247
    errno= EINVAL;
181
248
    return NULL;
182
249
  }
183
250
 
184
 
  memcached_st *ret= NULL;
185
251
  if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS)
186
252
  {
187
253
    return NULL;
188
254
  }
189
255
 
 
256
  memcached_st *ret= NULL;
190
257
  do
191
258
  {
192
259
    if (pool->firstfree > -1)
193
260
    {
194
 
      ret= pool->mmc[pool->firstfree--];
 
261
      ret= pool->server_pool[pool->firstfree--];
195
262
    }
196
263
    else if (pool->current_size == pool->size)
197
264
    {
198
 
      if (!block)
 
265
      if (not block)
199
266
      {
200
 
        *rc= mutex_exit(&pool->mutex);
 
267
        *rc= mutex_exit(&pool->mutex); // this should be a different error
201
268
        return NULL;
202
269
      }
203
270
 
210
277
        return NULL;
211
278
      }
212
279
    }
213
 
    else if (grow_pool(pool) == -1)
 
280
    else if (not grow_pool(pool))
214
281
    {
215
 
      *rc= mutex_exit(&pool->mutex);
 
282
      (void)mutex_exit(&pool->mutex);
 
283
      *rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
216
284
      return NULL;
217
285
    }
218
286
  }
223
291
  return ret;
224
292
}
225
293
 
226
 
memcached_return_t memcached_pool_push(memcached_pool_st* pool,
227
 
                                       memcached_st *mmc)
 
294
memcached_return_t memcached_pool_push(memcached_pool_st* pool, memcached_st *released)
228
295
{
229
 
  if (! pool)
 
296
  if (not pool)
230
297
    return MEMCACHED_INVALID_ARGUMENTS;
231
298
 
232
299
  memcached_return_t rc= mutex_enter(&pool->mutex);
234
301
  if (rc != MEMCACHED_SUCCESS)
235
302
    return rc;
236
303
 
237
 
  char* version= memcached_get_user_data(mmc);
238
304
  /* Someone updated the behavior on the object.. */
239
 
  if (version != pool->version)
 
305
  if (not pool->compare_version(released))
240
306
  {
241
 
    memcached_free(mmc);
242
 
    memset(mmc, 0, sizeof(*mmc));
243
 
    if (memcached_clone(mmc, pool->master) == NULL)
 
307
    memcached_free(released);
 
308
    if (not (released= memcached_clone(NULL, pool->master)))
244
309
    {
245
310
      rc= MEMCACHED_SOME_ERRORS;
246
311
    }
247
312
  }
248
313
 
249
 
  pool->mmc[++pool->firstfree]= mmc;
 
314
  pool->server_pool[++pool->firstfree]= released;
250
315
 
251
316
  if (pool->firstfree == 0 && pool->current_size == pool->size)
252
317
  {
266
331
                                               memcached_behavior_t flag,
267
332
                                               uint64_t data)
268
333
{
269
 
  if (! pool)
 
334
  if (not pool)
270
335
    return MEMCACHED_INVALID_ARGUMENTS;
271
336
 
272
337
  memcached_return_t rc= mutex_enter(&pool->mutex);
281
346
    return rc;
282
347
  }
283
348
 
284
 
  ++pool->version;
285
 
  memcached_set_user_data(pool->master, pool->version);
 
349
  pool->increment_version();
286
350
  /* update the clones */
287
351
  for (int xx= 0; xx <= pool->firstfree; ++xx)
288
352
  {
289
 
    rc= memcached_behavior_set(pool->mmc[xx], flag, data);
 
353
    rc= memcached_behavior_set(pool->server_pool[xx], flag, data);
290
354
    if (rc == MEMCACHED_SUCCESS)
291
355
    {
292
 
      memcached_set_user_data(pool->mmc[xx], pool->version);
 
356
      pool->server_pool[xx]->configure.version= pool->version();
293
357
    }
294
358
    else
295
359
    {
296
 
      memcached_free(pool->mmc[xx]);
297
 
      memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx]));
298
 
 
299
 
      if (memcached_clone(pool->mmc[xx], pool->master) == NULL)
 
360
      memcached_free(pool->server_pool[xx]);
 
361
      if (not (pool->server_pool[xx]= memcached_clone(NULL, pool->master)))
300
362
      {
301
363
        /* I'm not sure what to do in this case.. this would happen
302
364
          if we fail to push the server list inside the client..
304
366
          would work, except that you would add a hole in the pool list..
305
367
          in theory you could end up with an empty pool....
306
368
        */
307
 
        free(pool->mmc[xx]);
308
 
        pool->mmc[xx]= NULL;
309
369
      }
310
370
    }
311
371
  }