MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mock_server.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <sys/time.h>
6 #include <pthread.h>
7 #include <memcached/engine.h>
8 #include <memcached/extension.h>
9 #include <memcached/extension_loggers.h>
10 #include <mock_server.h>
11 
12 #define REALTIME_MAXDELTA 60*60*24*3
13 #define CONN_MAGIC 16369814453946373207ULL
14 
18 };
19 
20 struct mock_callbacks *mock_event_handlers[MAX_ENGINE_EVENT_TYPE + 1];
21 time_t process_started; /* when the mock server was started */
22 rel_time_t time_travel_offset;
23 rel_time_t current_time;
24 struct mock_connstruct *connstructs;
25 struct mock_extensions extensions;
26 EXTENSION_LOGGER_DESCRIPTOR *null_logger = NULL;
27 EXTENSION_LOGGER_DESCRIPTOR *stderr_logger = NULL;
28 ENGINE_HANDLE *engine = NULL;
29 
34 static void mock_get_auth_data(const void *cookie, auth_data_t *data) {
35  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
36  if (c != NULL) {
37  data->username = c->uname;
38  data->config = c->config;
39  }
40 }
41 
42 static void mock_store_engine_specific(const void *cookie, void *engine_data) {
43  if (cookie) {
44  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
45  assert(c->magic == CONN_MAGIC);
46  c->engine_data = engine_data;
47  }
48 }
49 
50 static void *mock_get_engine_specific(const void *cookie) {
51  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
52  assert(c == NULL || c->magic == CONN_MAGIC);
53  return c ? c->engine_data : NULL;
54 }
55 
56 static int mock_get_socket_fd(const void *cookie) {
57  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
58  return c->sfd;
59 }
60 
61 static void mock_set_tap_nack_mode(const void *cookie, bool enable) {
62  (void)cookie;
63  (void)enable;
64 }
65 
66 static void mock_cookie_reserve(const void *cookie) {
67  (void)cookie;
68 }
69 
70 static void mock_cookie_release(const void *cookie) {
71  (void)cookie;
72 }
73 
74 static const char *mock_get_server_version() {
75  return "mock server";
76 }
77 
78 static uint32_t mock_hash( const void *key, size_t length, const uint32_t initval) {
79  //this is a very stupid hash indeed
80  return 1;
81 }
82 
83 /* time-sensitive callers can call it by hand with this, outside the
84  normal ever-1-second timer */
85 static rel_time_t mock_get_current_time(void) {
86  struct timeval timer;
87  gettimeofday(&timer, NULL);
88  current_time = (rel_time_t) (timer.tv_sec - process_started + time_travel_offset);
89  return current_time;
90 }
91 
92 static rel_time_t mock_realtime(const time_t exptime) {
93  /* no. of seconds in 30 days - largest possible delta exptime */
94 
95  if (exptime == 0) return 0; /* 0 means never expire */
96 
97  if (exptime > REALTIME_MAXDELTA) {
98  /* if item expiration is at/before the server started, give it an
99  expiration time of 1 second after the server started.
100  (because 0 means don't expire). without this, we'd
101  underflow and wrap around to some large value way in the
102  future, effectively making items expiring in the past
103  really expiring never */
104  if (exptime <= process_started)
105  return (rel_time_t)1;
106  return (rel_time_t)(exptime - process_started);
107  } else {
108  return (rel_time_t)(exptime + mock_get_current_time());
109  }
110 }
111 
112 static void mock_notify_io_complete(const void *cookie, ENGINE_ERROR_CODE status) {
113  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
114  pthread_mutex_lock(&c->mutex);
115  c->status = status;
116  pthread_cond_signal(&c->cond);
117  pthread_mutex_unlock(&c->mutex);
118 }
119 
120 static time_t mock_abstime(const rel_time_t exptime)
121 {
122  return process_started + exptime;
123 }
124 
125 void mock_time_travel(int by) {
126  time_travel_offset += by;
127 }
128 
129 static int mock_parse_config(const char *str, struct config_item items[], FILE *error) {
130  return parse_config(str, items, error);
131 }
132 
137 static void *mock_new_independent_stats(void) {
138  struct mockstats *mockstats = calloc(sizeof(mockstats),1);
139  return mockstats;
140 }
141 
142 static void mock_release_independent_stats(void *stats) {
143  struct mockstats *mockstats = stats;
144  free(mockstats);
145 }
146 
147 static void mock_count_eviction(const void *cookie, const void *key, const int nkey) {
148  struct mock_connstruct *c = (struct mock_connstruct *)cookie;
149  c->evictions++;
150 }
151 
156 static bool mock_register_extension(extension_type_t type, void *extension)
157 {
158  if (extension == NULL) {
159  return false;
160  }
161 
162  switch (type) {
163  case EXTENSION_DAEMON:
164  for (EXTENSION_DAEMON_DESCRIPTOR *ptr = extensions.daemons;
165  ptr != NULL;
166  ptr = ptr->next) {
167  if (ptr == extension) {
168  return false;
169  }
170  }
171  ((EXTENSION_DAEMON_DESCRIPTOR *)(extension))->next = extensions.daemons;
172  extensions.daemons = extension;
173  return true;
174  case EXTENSION_LOGGER:
175  extensions.logger = extension;
176  return true;
177  default:
178  return false;
179  }
180 }
181 
182 static void mock_unregister_extension(extension_type_t type, void *extension)
183 {
184  switch (type) {
185  case EXTENSION_DAEMON:
186  {
187  EXTENSION_DAEMON_DESCRIPTOR *prev = NULL;
188  EXTENSION_DAEMON_DESCRIPTOR *ptr = extensions.daemons;
189 
190  while (ptr != NULL && ptr != extension) {
191  prev = ptr;
192  ptr = ptr->next;
193  }
194 
195  if (ptr != NULL && prev != NULL) {
196  prev->next = ptr->next;
197  }
198 
199  if (extensions.daemons == ptr) {
200  extensions.daemons = ptr->next;
201  }
202  }
203  break;
204  case EXTENSION_LOGGER:
205  if (extensions.logger == extension) {
206  if (stderr_logger == extension) {
207  extensions.logger = null_logger;
208  } else {
209  extensions.logger = stderr_logger;
210  }
211  }
212  break;
213 
214  default:
215  ;
216  }
217 
218 }
219 
220 static void* mock_get_extension(extension_type_t type)
221 {
222  switch (type) {
223  case EXTENSION_DAEMON:
224  return extensions.daemons;
225 
226  case EXTENSION_LOGGER:
227  return extensions.logger;
228 
229  default:
230  return NULL;
231  }
232 }
233 
238 static void mock_register_callback(ENGINE_HANDLE *eh,
239  ENGINE_EVENT_TYPE type,
240  EVENT_CALLBACK cb,
241  const void *cb_data) {
242  struct mock_callbacks *h =
243  calloc(sizeof(struct mock_callbacks), 1);
244  assert(h);
245  h->cb = cb;
246  h->cb_data = cb_data;
247  h->next = mock_event_handlers[type];
248  mock_event_handlers[type] = h;
249 }
250 
251 static void mock_perform_callbacks(ENGINE_EVENT_TYPE type,
252  const void *data,
253  const void *c) {
254  for (struct mock_callbacks *h = mock_event_handlers[type];
255  h; h = h->next) {
256  h->cb(c, type, data, h->cb_data);
257  }
258 }
259 
260 SERVER_HANDLE_V1 *get_mock_server_api(void)
261 {
262  static SERVER_CORE_API core_api = {
263  .server_version = mock_get_server_version,
264  .hash = mock_hash,
265  .realtime = mock_realtime,
266  .get_current_time = mock_get_current_time,
267  .abstime = mock_abstime,
268  .parse_config = mock_parse_config
269  };
270 
271  static SERVER_COOKIE_API server_cookie_api = {
272  .get_auth_data = mock_get_auth_data,
273  .store_engine_specific = mock_store_engine_specific,
274  .get_engine_specific = mock_get_engine_specific,
275  .get_socket_fd = mock_get_socket_fd,
276  .set_tap_nack_mode = mock_set_tap_nack_mode,
277  .notify_io_complete = mock_notify_io_complete,
278  .reserve = mock_cookie_reserve,
279  .release = mock_cookie_release
280  };
281 
282  static SERVER_STAT_API server_stat_api = {
283  .new_stats = mock_new_independent_stats,
284  .release_stats = mock_release_independent_stats,
285  .evicting = mock_count_eviction
286  };
287 
288  static SERVER_EXTENSION_API extension_api = {
289  .register_extension = mock_register_extension,
290  .unregister_extension = mock_unregister_extension,
291  .get_extension = mock_get_extension
292  };
293 
294  static SERVER_CALLBACK_API callback_api = {
295  .register_callback = mock_register_callback,
296  .perform_callbacks = mock_perform_callbacks
297  };
298 
299  static SERVER_HANDLE_V1 rv = {
300  .interface = 1,
301  .core = &core_api,
302  .stat = &server_stat_api,
303  .extension = &extension_api,
304  .callback = &callback_api,
305  .cookie = &server_cookie_api
306  };
307 
308  return &rv;
309 }
310 
311 void init_mock_server(ENGINE_HANDLE *server_engine) {
312  process_started = time(0);
313  null_logger = get_null_logger();
314  stderr_logger = get_stderr_logger();
315  engine = server_engine;
316  extensions.logger = null_logger;
317 }
318 
319 struct mock_connstruct *mk_mock_connection(const char *user, const char *config) {
320  struct mock_connstruct *rv = calloc(sizeof(struct mock_connstruct), 1);
321  auth_data_t ad;
322  assert(rv);
323  rv->magic = CONN_MAGIC;
324  rv->uname = user ? strdup(user) : NULL;
325  rv->config = config ? strdup(config) : NULL;
326  rv->connected = true;
327  rv->next = connstructs;
328  rv->evictions = 0;
329  rv->sfd = 0; //TODO make this more realistic
330  rv->status = ENGINE_SUCCESS;
331  connstructs = rv;
332  mock_perform_callbacks(ON_CONNECT, NULL, rv);
333  if (rv->uname) {
334  mock_get_auth_data(rv, &ad);
335  mock_perform_callbacks(ON_AUTH, (const void*)&ad, rv);
336  }
337 
338  assert(pthread_mutex_init(&rv->mutex, NULL) == 0);
339  assert(pthread_cond_init(&rv->cond, NULL) == 0);
340 
341  return rv;
342 }
343 
344 const void *create_mock_cookie(void) {
345  struct mock_connstruct *rv = calloc(sizeof(struct mock_connstruct), 1);
346  assert(rv);
347  rv->magic = CONN_MAGIC;
348  rv->connected = true;
349  rv->status = ENGINE_SUCCESS;
350  rv->handle_ewouldblock = true;
351  assert(pthread_mutex_init(&rv->mutex, NULL) == 0);
352  assert(pthread_cond_init(&rv->cond, NULL) == 0);
353 
354  return rv;
355 }
356 
357 void destroy_mock_cookie(const void *cookie) {
358  free((void*)cookie);
359 }
360 
361 void mock_set_ewouldblock_handling(const void *cookie, bool enable) {
362  struct mock_connstruct *v = (void*)cookie;
363  v->handle_ewouldblock = enable;
364 }
365 
366 void lock_mock_cookie(const void *cookie) {
367  struct mock_connstruct *c = (void*)cookie;
368  pthread_mutex_lock(&c->mutex);
369 }
370 
371 void unlock_mock_cookie(const void *cookie) {
372  struct mock_connstruct *c = (void*)cookie;
373  pthread_mutex_unlock(&c->mutex);
374 }
375 
376 void waitfor_mock_cookie(const void *cookie) {
377  struct mock_connstruct *c = (void*)cookie;
378  pthread_cond_wait(&c->cond, &c->mutex);
379 }
380 
381 void disconnect_mock_connection(struct mock_connstruct *c) {
382  c->connected = false;
383  mock_perform_callbacks(ON_DISCONNECT, NULL, c);
384 }
385 
386 void disconnect_all_mock_connections(struct mock_connstruct *c) {
387  if (c) {
388  disconnect_mock_connection(c);
389  disconnect_all_mock_connections(c->next);
390  free((void*)c->uname);
391  free((void*)c->config);
392  free(c);
393  }
394 }
395 
396 void destroy_mock_event_callbacks_rec(struct mock_callbacks *h) {
397  if (h) {
398  destroy_mock_event_callbacks_rec(h->next);
399  free(h);
400  }
401 }
402 
403 void destroy_mock_event_callbacks(void) {
404  int i = 0;
405  for (i = 0; i < MAX_ENGINE_EVENT_TYPE; i++) {
406  destroy_mock_event_callbacks_rec(mock_event_handlers[i]);
407  mock_event_handlers[i] = NULL;
408  }
409 }
410