-
Notifications
You must be signed in to change notification settings - Fork 215
/
zoo_lock.c
428 lines (397 loc) · 13.2 KB
/
zoo_lock.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef DLL_EXPORT
#define USE_STATIC_LIB
#endif
#if defined(__CYGWIN__)
#define USE_IPV6
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <zookeeper_log.h>
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <limits.h>
#include <zoo_lock.h>
#include <stdbool.h>
#ifdef HAVE_SYS_UTSNAME_H
#include <sys/utsname.h>
#endif
#ifdef HAVE_GETPWUID_R
#include <pwd.h>
#endif
#define IF_DEBUG(x) if (logLevel==ZOO_LOG_LEVEL_DEBUG) {x;}
ZOOAPI int zkr_lock_init(zkr_lock_mutex_t* mutex, zhandle_t* zh,
char* path, struct ACL_vector *acl) {
mutex->zh = zh;
mutex->path = path;
mutex->acl = acl;
mutex->completion = NULL;
mutex->cbdata = NULL;
mutex->id = NULL;
mutex->ownerid = NULL;
mutex->isOwner = 0;
pthread_mutex_init(&(mutex->pmutex), NULL);
return 0;
}
ZOOAPI int zkr_lock_init_cb(zkr_lock_mutex_t *mutex, zhandle_t* zh,
char *path, struct ACL_vector *acl,
zkr_lock_completion completion, void* cbdata) {
mutex->zh = zh;
mutex->path = path;
mutex->acl = acl;
mutex->completion = completion;
mutex->cbdata = cbdata;
mutex->isOwner = 0;
mutex->ownerid = NULL;
mutex->id = NULL;
pthread_mutex_init(&(mutex->pmutex), NULL);
return 0;
}
/**
* unlock the mutex
*/
ZOOAPI int zkr_lock_unlock(zkr_lock_mutex_t *mutex) {
pthread_mutex_lock(&(mutex->pmutex));
zhandle_t *zh = mutex->zh;
if (mutex->id != NULL) {
int len = strlen(mutex->path) + strlen(mutex->id) + 2;
char buf[len];
sprintf(buf, "%s/%s", mutex->path, mutex->id);
int ret = 0;
int count = 0;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = (.5)*1000000;
ret = ZCONNECTIONLOSS;
while (ret == ZCONNECTIONLOSS && (count < 3)) {
ret = zoo_delete(zh, buf, -1);
if (ret == ZCONNECTIONLOSS) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_DEBUG(LOGCALLBACK(zh), ("connectionloss while deleting the node"));
#else
LOG_DEBUG(("connectionloss while deleting the node"));
#endif
nanosleep(&ts, 0);
count++;
}
}
if (ret == ZOK || ret == ZNONODE) {
zkr_lock_completion completion = mutex->completion;
if (completion != NULL) {
completion(1, mutex->cbdata);
}
free(mutex->id);
mutex->id = NULL;
pthread_mutex_unlock(&(mutex->pmutex));
return 0;
}
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_WARN(LOGCALLBACK(zh), ("not able to connect to server - giving up"));
#else
LOG_WARN(("not able to connect to server - giving up"));
#endif
pthread_mutex_unlock(&(mutex->pmutex));
return ZCONNECTIONLOSS;
}
pthread_mutex_unlock(&(mutex->pmutex));
return ZSYSTEMERROR;
}
static void free_String_vector(struct String_vector *v) {
if (v->data) {
int32_t i;
for (i=0; i<v->count; i++) {
free(v->data[i]);
}
free(v->data);
v->data = 0;
}
}
static int vstrcmp(const void* str1, const void* str2) {
const char **a = (const char**)str1;
const char **b = (const char**) str2;
return strcmp(strrchr(*a, '-')+1, strrchr(*b, '-')+1);
}
static void sort_children(struct String_vector *vector) {
qsort( vector->data, vector->count, sizeof(char*), &vstrcmp);
}
static char* child_floor(char **sorted_data, int len, char *element) {
char* ret = NULL;
int i =0;
for (i=0; i < len; i++) {
if (strcmp(sorted_data[i], element) < 0) {
ret = sorted_data[i];
}
}
return ret;
}
static void lock_watcher_fn(zhandle_t* zh, int type, int state,
const char* path, void *watcherCtx) {
//callback that we registered
//should be called
zkr_lock_lock((zkr_lock_mutex_t*) watcherCtx);
}
/**
* get the last name of the path
*/
static char* getName(char* str) {
char* name = strrchr(str, '/');
if (name == NULL)
return NULL;
return strdup(name + 1);
}
/**
* just a method to retry get children
*/
static int retry_getchildren(zhandle_t *zh, char* path, struct String_vector *vector,
struct timespec *ts, int retry) {
int ret = ZCONNECTIONLOSS;
int count = 0;
while (ret == ZCONNECTIONLOSS && count < retry) {
ret = zoo_get_children(zh, path, 0, vector);
if (ret == ZCONNECTIONLOSS) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_DEBUG(LOGCALLBACK(zh), ("connection loss to the server"));
#else
LOG_DEBUG(("connection loss to the server"));
#endif
nanosleep(ts, 0);
count++;
}
}
return ret;
}
/** see if our node already exists
* if it does then we dup the name and
* return it
*/
static char* lookupnode(struct String_vector *vector, char *prefix) {
char *ret = NULL;
if (vector->data) {
int i = 0;
for (i = 0; i < vector->count; i++) {
char* child = vector->data[i];
if (strncmp(prefix, child, strlen(prefix)) == 0) {
ret = strdup(child);
break;
}
}
}
return ret;
}
/** retry zoo_wexists
*/
static int retry_zoowexists(zhandle_t *zh, char* path, watcher_fn watcher, void* ctx,
struct Stat *stat, struct timespec *ts, int retry) {
int ret = ZCONNECTIONLOSS;
int count = 0;
while (ret == ZCONNECTIONLOSS && count < retry) {
ret = zoo_wexists(zh, path, watcher, ctx, stat);
if (ret == ZCONNECTIONLOSS) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_DEBUG(LOGCALLBACK(zh), ("connectionloss while setting watch on my predecessor"));
#else
LOG_DEBUG(("connectionloss while setting watch on my predecessor"));
#endif
nanosleep(ts, 0);
count++;
}
}
return ret;
}
/**
* the main code that does the zookeeper leader
* election. this code creates its own ephemeral
* node on the given path and sees if its the first
* one on the list and claims to be a leader if and only
* if its the first one of children in the paretn path
*/
static int zkr_lock_operation(zkr_lock_mutex_t *mutex, struct timespec *ts) {
zhandle_t *zh = mutex->zh;
char *path = mutex->path;
char *id = mutex->id;
struct Stat stat;
char* owner_id = NULL;
int retry = 3;
do {
const clientid_t *cid = zoo_client_id(zh);
// get the session id
int64_t session = cid->client_id;
char prefix[30];
int ret = 0;
#if defined(__x86_64__)
snprintf(prefix, 30, "x-%016lx-", session);
#else
snprintf(prefix, 30, "x-%016llx-", session);
#endif
struct String_vector vectorst;
vectorst.data = NULL;
vectorst.count = 0;
ret = ZCONNECTIONLOSS;
ret = retry_getchildren(zh, path, &vectorst, ts, retry);
if (ret != ZOK)
return ret;
struct String_vector *vector = &vectorst;
mutex->id = lookupnode(vector, prefix);
free_String_vector(vector);
if (mutex->id == NULL) {
int len = strlen(path) + strlen(prefix) + 2;
char buf[len];
char retbuf[len+20];
snprintf(buf, len, "%s/%s", path, prefix);
ret = ZCONNECTIONLOSS;
ret = zoo_create(zh, buf, NULL, 0, mutex->acl,
ZOO_EPHEMERAL|ZOO_SEQUENCE, retbuf, (len+20));
// do not want to retry the create since
// we would end up creating more than one child
if (ret != ZOK) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_WARN(LOGCALLBACK(zh), ("could not create zoo node %s", buf));
#else
LOG_WARN(("could not create zoo node %s", buf));
#endif
return ret;
}
mutex->id = getName(retbuf);
}
if (mutex->id != NULL) {
ret = ZCONNECTIONLOSS;
ret = retry_getchildren(zh, path, vector, ts, retry);
if (ret != ZOK) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_WARN(LOGCALLBACK(zh), ("could not connect to server"));
#else
LOG_WARN(("could not connect to server"));
#endif
return ret;
}
//sort this list
sort_children(vector);
owner_id = vector->data[0];
mutex->ownerid = strdup(owner_id);
id = mutex->id;
char* lessthanme = child_floor(vector->data, vector->count, id);
if (lessthanme != NULL) {
int flen = strlen(mutex->path) + strlen(lessthanme) + 2;
char last_child[flen];
sprintf(last_child, "%s/%s",mutex->path, lessthanme);
ret = ZCONNECTIONLOSS;
ret = retry_zoowexists(zh, last_child, &lock_watcher_fn, mutex,
&stat, ts, retry);
// cannot watch my predecessor i am giving up
// we need to be able to watch the predecessor
// since if we do not become a leader the others
// will keep waiting
if (ret != ZOK) {
free_String_vector(vector);
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_WARN(LOGCALLBACK(zh), ("unable to watch my predecessor"));
#else
LOG_WARN(("unable to watch my predecessor"));
#endif
ret = zkr_lock_unlock(mutex);
while (ret == 0) {
//we have to give up our leadership
// since we cannot watch out predecessor
ret = zkr_lock_unlock(mutex);
}
return ret;
}
// we are not the owner of the lock
mutex->isOwner = 0;
}
else {
// this is the case when we are the owner
// of the lock
if (strcmp(mutex->id, owner_id) == 0) {
#if ZOO_MAJOR_VERSION>=3 && ZOO_MINOR_VERSION>=5
LOG_DEBUG(LOGCALLBACK(zh), ("got the zoo lock owner - %s", mutex->id));
#else
LOG_DEBUG(("got the zoo lock owner - %s", mutex->id));
#endif
mutex->isOwner = 1;
if (mutex->completion != NULL) {
mutex->completion(0, mutex->cbdata);
}
return ZOK;
}
}
free_String_vector(vector);
return ZOK;
}
} while (mutex->id == NULL);
return ZOK;
}
ZOOAPI int zkr_lock_lock(zkr_lock_mutex_t *mutex) {
pthread_mutex_lock(&(mutex->pmutex));
zhandle_t *zh = mutex->zh;
char *path = mutex->path;
struct Stat stat;
int exists = zoo_exists(zh, path, 0, &stat);
int count = 0;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = (.5)*1000000;
// retry to see if the path exists and
// and create if the path does not exist
while ((exists == ZCONNECTIONLOSS || exists == ZNONODE) && (count <4)) {
count++;
// retry the operation
if (exists == ZCONNECTIONLOSS)
exists = zoo_exists(zh, path, 0, &stat);
else if (exists == ZNONODE)
exists = zoo_create(zh, path, NULL, 0, mutex->acl, 0, NULL, 0);
nanosleep(&ts, 0);
}
// need to check if we cannot still access the server
int check_retry = ZCONNECTIONLOSS;
count = 0;
while (check_retry != ZOK && count <4) {
check_retry = zkr_lock_operation(mutex, &ts);
if (check_retry != ZOK) {
nanosleep(&ts, 0);
count++;
}
}
pthread_mutex_unlock(&(mutex->pmutex));
return zkr_lock_isowner(mutex);
}
ZOOAPI char* zkr_lock_getpath(zkr_lock_mutex_t *mutex) {
return mutex->path;
}
ZOOAPI int zkr_lock_isowner(zkr_lock_mutex_t *mutex) {
return (mutex->id != NULL && mutex->ownerid != NULL
&& (strcmp(mutex->id, mutex->ownerid) == 0));
}
ZOOAPI char* zkr_lock_getid(zkr_lock_mutex_t *mutex) {
return mutex->ownerid;
}
ZOOAPI int zkr_lock_destroy(zkr_lock_mutex_t* mutex) {
if (mutex->id)
free(mutex->id);
mutex->path = NULL;
mutex->acl = NULL;
mutex->completion = NULL;
pthread_mutex_destroy(&(mutex->pmutex));
mutex->isOwner = 0;
if (mutex->ownerid)
free(mutex->ownerid);
return 0;
}