├── .gitignore ├── Makefile ├── README ├── TODO ├── apps ├── Makefile └── queue │ ├── Makefile │ ├── README.md │ ├── adler32.c │ └── queue.c ├── conductor ├── Makefile ├── acrd_priv.h ├── acrdnet.c ├── acrdops.c ├── conductor.c ├── logger.c ├── logger.h ├── store.c └── store.h ├── include ├── Makefile ├── accord.h ├── accord_proto.h ├── coroutine.h ├── event.h ├── list.h ├── net.h ├── util.h └── work.h ├── lib ├── Makefile ├── coroutine.c ├── event.c ├── net.c └── work.c ├── libacrd ├── Makefile ├── libacrd.c ├── libacrd.map └── libacrd.pc.in └── test ├── Makefile ├── acrdbench.c ├── test-aio.c ├── test-concurrent.c ├── test-coroutine.c ├── test-io.c ├── test-txn.c ├── test-watch.c ├── watch_files.c └── watch_nodes.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.[oda] 2 | *.so 3 | cli 4 | *swp 5 | GPATH 6 | GRTAGS 7 | GSYMS 8 | GTAGS 9 | trash 10 | .gdbinit 11 | conductor/conductor 12 | test/test 13 | test/acrdbench 14 | test/test-coroutine 15 | test/test-all 16 | test/test-concurrent 17 | test/test-txn 18 | test/test-io 19 | test/test-aio 20 | test/test-watch 21 | test/watch_nodes 22 | test/watch_files 23 | valgrind.log 24 | test/test-report.*ml 25 | libacrd/libacrd.pc 26 | apps/queue/qbench 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION ?= 0.0.1 2 | 3 | PREFIX ?= /usr 4 | 5 | export VERSION PREFIX 6 | 7 | .PHONY:all 8 | all: 9 | $(MAKE) -C libacrd 10 | $(MAKE) -C conductor 11 | $(MAKE) -C test 12 | $(MAKE) -C apps 13 | 14 | .PHONY:clean 15 | clean: 16 | $(MAKE) -C libacrd clean 17 | $(MAKE) -C conductor clean 18 | $(MAKE) -C test clean 19 | $(MAKE) -C apps clean 20 | $(MAKE) -C lib clean 21 | 22 | .PHONY:install 23 | install: 24 | $(MAKE) -C libacrd install 25 | $(MAKE) -C conductor install 26 | $(MAKE) -C include install 27 | 28 | .PHONY:apps 29 | apps: 30 | $(MAKE) -C apps 31 | 32 | .PHONY:test 33 | test: 34 | $(MAKE) -C libacrd 35 | $(MAKE) -C test test 36 | 37 | .PHONY:check 38 | check: test 39 | $(MAKE) -C test check 40 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ** Accord project ** 2 | 3 | NOTE: Now, this project is marked as deprecated, because we've observed that write-intensive 4 | workload is much less important than read-intensive workload for coordination service. 5 | Please use ZooKeeper. 6 | 7 | * Overview 8 | ---------- 9 | - Accord is a coordination service(like ZooKeeper). It features : 10 | 11 | 1. Accord focuses on write-intensive workloads unlike ZooKeeper. ZooKeeper forwards all write requests to a master server. It can be bottleneck in write-intensive workloads. The below benchmark demonstrates that the write-operation throughput of Accord is much higher than one of ZooKeeper (up to 20 times better throughput at persistent mode, and up to 18 times better throughput at in-memory mode). 12 | 2. More flexible transaction support. Not only write, del operations, but also cmp, copy, read operations are supported in transaction operations. 13 | 3. In-memory mode and persistent mode support. 14 | 4. Message size is unbounded, and partial update is supported. 15 | 16 | 17 | * Project Page 18 | ---------- 19 | - Getting started / Performance benchmark is avalable from our project page. Please visit http://www.osrg.net/accord 20 | 21 | * Directory structure 22 | ---------- 23 | - conductor : Server-side daemon 24 | - libacrd : Client-side library 25 | - test : Test and benchmark programs. Files in this directory are useful as sample programs of Accord. 26 | - include : Header files 27 | - lib : Utility library 28 | 29 | * API Documentation 30 | ---------- 31 | - Please see include/accord.h or http://www.osrg.net/accord/api.txt 32 | - Programs in ACRD_ROOT/test are very useful to understand how to use accord API. 33 | 34 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Server-side TODO list 2 | -------- 3 | - Dynamic server adding. 4 | 5 | * Client-side TODO list 6 | -------- 7 | - Implementing Distributed Queue. 8 | -- Provides DB log manager API. 9 | - More benchmark. 10 | - Buffer management at callback threads. 11 | -- This is for supporting ZooKeeper API. 12 | - ZooKeeper API support. 13 | -- Project is launched at : https://github.com/oza/zkproxy 14 | 15 | * General TODO items 16 | -------- 17 | - Write documentation on github. 18 | -------------------------------------------------------------------------------- /apps/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:all 2 | all: 3 | $(MAKE) -C queue 4 | 5 | .PHONY:clean 6 | clean: 7 | $(MAKE) -C queue clean 8 | 9 | .PHONY:queue 10 | queue: 11 | $(MAKE) -C queue 12 | 13 | -------------------------------------------------------------------------------- /apps/queue/Makefile: -------------------------------------------------------------------------------- 1 | sbindir ?= $(PREFIX)/sbin 2 | 3 | CFLAGS += -g -O3 -Wall -Wstrict-prototypes -I../../include 4 | CFLAGS += -D_GNU_SOURCE -DNDEBUG 5 | LIBS += -L../../libacrd -lpthread -lacrd 6 | 7 | PROGRAMS = qbench 8 | QUEUE_OBJS = queue.o 9 | QUEUE_DEP = $(ACCORD_OBJS:.o=.d) 10 | 11 | .PHONY:all 12 | all: $(PROGRAMS) 13 | 14 | qbench: $(QUEUE_OBJS) 15 | $(CC) $(CFLAGS) $^ -o $@ $(LIBS) 16 | 17 | -include $(ACCORD_DEP) 18 | 19 | %.o: %.c 20 | $(CC) -c $(CFLAGS) $*.c -o $*.o 21 | @$(CC) -MM $(CFLAGS) -MF $*.d -MT $*.o $*.c 22 | 23 | .PHONY:clean 24 | clean: 25 | rm -f *.[od] $(PROGRAMS) 26 | 27 | # support for GNU Flymake 28 | check-syntax: 29 | $(CC) $(CFLAGS) -fsyntax-only $(CHK_SOURCES) 30 | -------------------------------------------------------------------------------- /apps/queue/README.md: -------------------------------------------------------------------------------- 1 | # How to running queue 2 | 3 | 1. Please install accord. 4 | 2. run as follows 5 | 6 | ``` 7 | $ make apps 8 | $ ./apps/qbench localhost 9090 200 10000 9 | 2000000 requests in 298.100236 sec. (6709.15 throughput) 10 | ``` 11 | 12 | ## Bencmark result 13 | 14 | * The benchmark environment 15 | * Xeon 2.1 GHz 4 Core 16 | * 7200 rpm HDD 17 | * Run Accord as disk persistency mode with single node 18 | 19 | the result of the benchmarks(50% push and 50% pop) is as follows: 20 | 21 | ``` 22 | 2000000 requests in 298.100236 sec. (6709.15 throughput) 23 | ``` 24 | 25 | 26 | Additionally, the other benchmarks(100% push) achieve better result as follows: 27 | 28 | ``` 29 | 2000000 requests in 167.953943 sec. (11908.03 throughput) 30 | ``` 31 | 32 | To benchmark only push(), please comment out queue_pop() function in the run() function. 33 | 34 | ## TODO 35 | 36 | * run benchmark with 1KB message 37 | -------------------------------------------------------------------------------- /apps/queue/adler32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a modified version based on adler32.c from gst-ffmpeg based on 3 | * adler32.c from the zlib library. 4 | * 5 | * Copyright (C) 1995 Mark Adler 6 | * 7 | * This software is provided 'as-is', without any express or implied 8 | * warranty. In no event will the authors be held liable for any damages 9 | * arising from the use of this software. 10 | * 11 | * Permission is granted to anyone to use this software for any purpose, 12 | * including commercial applications, and to alter it and redistribute it 13 | * freely, subject to the following restrictions: 14 | * 15 | * 1. The origin of this software must not be misrepresented; you must not 16 | * claim that you wrote the original software. If you use this software 17 | * in a product, an acknowledgment in the product documentation would be 18 | * appreciated but is not required. 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | * 23 | */ 24 | #include 25 | 26 | #define ADLER32_BASE 65521L /* largest prime smaller than 65536 */ 27 | 28 | #define ADLER32_DO1(buf) {s1 += *buf++; s2 += s1;} 29 | #define ADLER32_DO4(buf) ADLER32_DO1(buf); ADLER32_DO1(buf); ADLER32_DO1(buf); ADLER32_DO1(buf); 30 | #define ADLER32_DO16(buf) ADLER32_DO4(buf); ADLER32_DO4(buf); ADLER32_DO4(buf); ADLER32_DO4(buf); 31 | 32 | static uint32_t adler32(uint32_t adler, const void* ptr, uint32_t len) 33 | { 34 | const uint8_t* buf = (const uint8_t*)ptr; 35 | uint32_t s1 = adler & 0xffff; 36 | uint32_t s2 = (adler >> 16) & 0xffff; 37 | 38 | while (len > 0) { 39 | while(len > 16 && s2 < (1U<<31)) { 40 | ADLER32_DO16(buf); len-=16; 41 | } 42 | ADLER32_DO1(buf); len--; 43 | s1 %= ADLER32_BASE; 44 | s2 %= ADLER32_BASE; 45 | } 46 | return (s2 << 16) | s1; 47 | } 48 | -------------------------------------------------------------------------------- /apps/queue/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include "util.h" 8 | #include "coroutine.h" 9 | #include "adler32.c" 10 | 11 | #define NR_THREADS 100 12 | 13 | static char *hostname; 14 | static int port; 15 | 16 | struct acrd_path_list_entry { 17 | char *path; 18 | 19 | struct list_head list; 20 | }; 21 | 22 | struct queue_handle { 23 | struct acrd_handle *ah; 24 | char name[128]; 25 | }; 26 | 27 | struct queue_msg { 28 | uint32_t len; 29 | uint32_t checksum; 30 | char data[0]; 31 | }; 32 | 33 | struct ack_info { 34 | char *invisible_path; 35 | }; 36 | 37 | static void test_concurrent_list_cb(struct acrd_handle *h, const char *path, void *arg) 38 | { 39 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 40 | struct list_head *head = arg; 41 | 42 | entry->path = strdup(path); 43 | list_add_tail(&entry->list, head); 44 | } 45 | 46 | static int create_first_nodes(struct acrd_handle *h) 47 | { 48 | int max = 1; 49 | int ret; 50 | struct acrd_tx *tx; 51 | 52 | tx = acrd_tx_init(h); 53 | 54 | retry: 55 | acrd_tx_write(tx, "/tmp/queue/min", &max, sizeof(max), 0, 56 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 57 | acrd_tx_write(tx, "/tmp/queue/max", &max, sizeof(max), 0, 58 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 59 | ret = acrd_tx_commit(tx, 0); 60 | 61 | if (ret != ACRD_SUCCESS && ret != ACRD_ERR_EXIST) { 62 | if (ret == ACRD_ERR_AGAIN) { 63 | goto retry; 64 | } else 65 | printf("%d:unknown err %d.\n", __LINE__, ret); 66 | } 67 | acrd_tx_close(tx); 68 | return 0; 69 | } 70 | 71 | struct queue_handle *queue_init(const char *hostname, int port, const char *name) 72 | { 73 | struct queue_handle *qh; 74 | struct acrd_handle *ah; 75 | 76 | ah = acrd_init(hostname, port, NULL, NULL, NULL); 77 | qh = zalloc(sizeof(struct queue_handle)); 78 | 79 | if (!qh || !ah) 80 | return NULL; 81 | 82 | qh->ah = ah; 83 | create_first_nodes(ah); 84 | return qh; 85 | } 86 | 87 | void queue_close(struct queue_handle *qh) 88 | { 89 | acrd_close(qh->ah); 90 | free(qh); 91 | 92 | return; 93 | } 94 | 95 | inline uint32_t calc_checksum(void *data, uint32_t len) 96 | { 97 | const uint32_t adler = 1; 98 | return adler32(adler, data, len);; 99 | } 100 | 101 | inline uint32_t get_msghdr_size(void) 102 | { 103 | return sizeof(struct queue_msg); 104 | } 105 | 106 | int queue_push(struct queue_handle *qh, void *data, uint32_t len) 107 | { 108 | struct acrd_tx *tx; 109 | char retdata[32]; 110 | uint32_t size, max; 111 | int ret; 112 | char path[256]; 113 | struct acrd_handle *h = qh->ah; 114 | uint32_t delta = 1; 115 | struct queue_msg *qe; 116 | 117 | qe = zalloc(sizeof(struct queue_msg) + len); 118 | memcpy(qe->data, data, len); 119 | qe->len = len; 120 | qe->checksum = calc_checksum(qe->data, qe->len); 121 | //printf("len %d checksum %d data %s \n", qe->len, qe->checksum, qe->data); 122 | 123 | size = sizeof(retdata); 124 | retry1: 125 | assert(max > 0); 126 | 127 | tx = acrd_tx_init(h); 128 | acrd_tx_atomic_inc(tx, "/tmp/queue/max", &delta, 129 | sizeof(uint32_t), 0, 0); 130 | acrd_tx_read(tx, "/tmp/queue/max", &retdata, &size, 0, 0); 131 | ret = acrd_tx_commit(tx, 0); 132 | if (ret != ACRD_SUCCESS) { 133 | if (ret == ACRD_ERR_AGAIN) 134 | goto retry1; 135 | else 136 | printf("%d:unknown err %d.\n", __LINE__, ret); 137 | } 138 | acrd_tx_close(tx); 139 | memcpy(&max, retdata, sizeof(max)); 140 | sprintf(path, "/tmp/queue/%d", max - 1); 141 | //printf("max %d\n", max); 142 | //printf("wrote data path %s len %d checksum %d data %s \n", path, qe->len, qe->checksum, qe->data); 143 | 144 | retry2: 145 | ret = acrd_write(h, path, qe, get_msghdr_size() + len, 0, 146 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 147 | if (ret != ACRD_SUCCESS) { 148 | if (ret == ACRD_ERR_AGAIN) 149 | goto retry2; 150 | else { 151 | printf("write err?\n"); 152 | free(qe); 153 | return -1; 154 | } 155 | } 156 | return 0; 157 | } 158 | 159 | int queue_pop(struct queue_handle *qh, struct queue_msg **retqe) 160 | { 161 | struct acrd_handle *h = qh->ah; 162 | struct acrd_tx *tx; 163 | int delta = 1, min = 0, ret; 164 | char path[256]; 165 | char min_buf[32]; 166 | uint32_t size = sizeof(uint32_t); 167 | uint32_t min_size = sizeof(uint32_t); 168 | uint32_t checksum; 169 | uint32_t qe_size; 170 | uint64_t offset; 171 | struct queue_msg *qe; 172 | 173 | *retqe = NULL; 174 | qe = zalloc(sizeof(struct queue_msg)); 175 | qe_size = sizeof(*qe); 176 | 177 | if (qe == NULL) { 178 | printf("oom\n"); 179 | return -1; 180 | } 181 | retry1: 182 | tx = acrd_tx_init(h); 183 | acrd_tx_read(tx, "/tmp/queue/min", min_buf, &min_size, 0, 0); 184 | acrd_tx_atomic_inc(tx, "/tmp/queue/min", &delta, 185 | sizeof(uint32_t), 0, 0); 186 | ret = acrd_tx_commit(tx, 0); 187 | acrd_tx_close(tx); 188 | switch (ret) { 189 | case ACRD_SUCCESS: 190 | break; 191 | case ACRD_ERR_AGAIN: 192 | goto retry1; 193 | case ACRD_ERR_NOTFOUND: 194 | default: 195 | printf("the node min is not found\n"); 196 | free(qe); 197 | return -1; 198 | } 199 | 200 | if (size != sizeof(min)) { 201 | printf("the read min size error\n"); 202 | return -1; 203 | } 204 | 205 | memcpy(&min, min_buf, sizeof(min)); 206 | //printf("min %d max %d\n", min, max); 207 | if (min < 0) { 208 | printf("min value error\n"); 209 | return -1; 210 | } 211 | 212 | sprintf(path, "/tmp/queue/%d", min); 213 | //printf("path %s min %d max %d\n", path, min, max); 214 | 215 | retry2: 216 | ret = acrd_read(h, path, qe, &qe_size, 0, 0); 217 | if (ret != ACRD_SUCCESS) { 218 | if (ret == ACRD_ERR_AGAIN) 219 | goto retry2; 220 | else { 221 | printf("%d:unknown err %d.\n", __LINE__, ret); 222 | return -1; 223 | } 224 | } 225 | 226 | /* read body */ 227 | qe = realloc(qe, get_msghdr_size() + qe->len); 228 | qe_size = qe->len; 229 | offset = get_msghdr_size(); 230 | retry3: 231 | ret = acrd_read(h, path, qe->data, &qe_size, offset, 0); 232 | if (ret != ACRD_SUCCESS) { 233 | if (ret == ACRD_ERR_AGAIN) 234 | goto retry3; 235 | else { 236 | printf("%d:unknown err %d.\n", __LINE__, ret); 237 | return -1; 238 | } 239 | } 240 | 241 | checksum = calc_checksum(qe->data, qe->len); 242 | //printf("len %d checksum %d data %s\n", qe->len, qe->checksum, qe->data); 243 | 244 | if (qe->checksum != checksum) { 245 | printf("Read corrupt data\n"); 246 | printf("path %s data %s len %d checksum %d, calc value : %d\n", 247 | path, qe->data, qe->len, 248 | qe->checksum, checksum); 249 | return -1; 250 | } 251 | *retqe = qe; 252 | 253 | /* FIXME: it is better to call queue_ack() */ 254 | ret = acrd_del(h, path, 0); 255 | 256 | return 0; 257 | } 258 | 259 | int queue_ack(struct queue_handle *qh, struct ack_info *info) 260 | { 261 | int ret; 262 | struct acrd_handle *h = qh->ah; 263 | char *inv_path = info->invisible_path; 264 | 265 | ret = acrd_del(h, inv_path, 0); 266 | if (ret != ACRD_SUCCESS) 267 | return -1; 268 | 269 | free(info->invisible_path); 270 | free(info); 271 | return 0; 272 | } 273 | 274 | void queue_msg_close(struct queue_msg *msg) 275 | { 276 | free(msg); 277 | } 278 | 279 | static void *run(void *arg) 280 | { 281 | struct queue_handle *h; 282 | const char *qname = "hoge"; 283 | char data[128] = "the contents of data"; 284 | uint32_t size = strlen(data) + 1; 285 | struct queue_msg *msg; 286 | int reqs, i; 287 | 288 | reqs = *(int *)arg; 289 | h = queue_init(hostname, port, qname); 290 | if (h == NULL) { 291 | printf("failed to exit..."); 292 | goto exit; 293 | } 294 | 295 | /* very simple test case */ 296 | for (i = 0; i < reqs; i++) { 297 | queue_push(h, data, size); 298 | if (queue_pop(h, &msg) == 0) { 299 | if (msg) 300 | queue_msg_close(msg); 301 | } 302 | } 303 | printf("exit.\n"); 304 | 305 | /* cleanup */ 306 | queue_close(h); 307 | 308 | exit: 309 | pthread_exit(NULL); 310 | } 311 | 312 | int main(int argc, char *argv[]) { 313 | int ret, i, nr_threads, nr_requests; 314 | struct acrd_handle *h; 315 | LIST_HEAD(path_list); 316 | struct acrd_listcb listcb = { 317 | .cb = test_concurrent_list_cb, 318 | .arg = &path_list, 319 | }; 320 | struct acrd_path_list_entry *entry, *n; 321 | struct timeval start, end, total; 322 | double throughput; 323 | 324 | pthread_t *th; 325 | 326 | if (argc < 5) { 327 | printf("usage: ./qbench [hostname] [port] [nr_threads] [nr_requests]\n"); 328 | exit(1); 329 | } 330 | 331 | hostname = argv[1]; 332 | port = atoi(argv[2]); 333 | nr_threads = atoi(argv[3]); 334 | nr_requests = atoi(argv[4]); 335 | th = malloc(sizeof(pthread_t)*nr_threads); 336 | if (!th) { 337 | printf("oom\n"); 338 | exit(1); 339 | } 340 | 341 | gettimeofday(&start, NULL); 342 | for (i = 0; i < nr_threads; i++) { 343 | ret = pthread_create(&th[i], NULL, run, &nr_requests); 344 | if (ret < 0) { 345 | printf("failed to init threads.\n"); 346 | exit(1); 347 | } 348 | } 349 | 350 | for (i = 0; i < nr_threads; i++) 351 | pthread_join(th[i], NULL); 352 | 353 | gettimeofday(&end, NULL); 354 | timersub(&end, &start, &total); 355 | throughput = (nr_requests * nr_threads) / 356 | (total.tv_sec + ((double)total.tv_usec)/1000000.0); 357 | 358 | printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n", 359 | nr_requests * nr_threads, (int)total.tv_sec, (int)total.tv_usec, 360 | throughput); 361 | 362 | /* cleanup data */ 363 | h = acrd_init(hostname, port, NULL, NULL, NULL); 364 | acrd_list(h, "/tmp/", 0, &listcb); 365 | list_for_each_entry_safe(entry, n, &path_list, list) { 366 | acrd_del(h, entry->path, 0); 367 | free(entry->path); 368 | list_del(&entry->list); 369 | free(entry); 370 | } 371 | acrd_close(h); 372 | return 0; 373 | } 374 | -------------------------------------------------------------------------------- /conductor/Makefile: -------------------------------------------------------------------------------- 1 | sbindir ?= $(PREFIX)/sbin 2 | 3 | CFLAGS += -g -O3 -Wall -Wstrict-prototypes -I../include 4 | CFLAGS += -D_GNU_SOURCE -DNDEBUG 5 | LIBS += -lpthread -ldb -lcpg 6 | 7 | PROGRAMS = conductor 8 | ACCORD_OBJS = store.o conductor.o acrdnet.o acrdops.o logger.o \ 9 | ../lib/event.o ../lib/net.o ../lib/work.o ../lib/coroutine.o 10 | ACCORD_DEP = $(ACCORD_OBJS:.o=.d) 11 | 12 | .PHONY:all 13 | all: $(PROGRAMS) 14 | 15 | conductor: $(ACCORD_OBJS) 16 | $(CC) $^ -o $@ $(LIBS) 17 | 18 | -include $(ACCORD_DEP) 19 | 20 | %.o: %.c 21 | $(CC) -c $(CFLAGS) $*.c -o $*.o 22 | @$(CC) -MM $(CFLAGS) -MF $*.d -MT $*.o $*.c 23 | 24 | .PHONY:clean 25 | clean: 26 | rm -f *.[od] $(PROGRAMS) 27 | 28 | .PHONY:install 29 | install: $(PROGRAMS) 30 | install -d -m 755 $(DESTDIR)$(sbindir) 31 | install -m 755 $(PROGRAMS) $(DESTDIR)$(sbindir) 32 | 33 | # support for GNU Flymake 34 | check-syntax: 35 | $(CC) $(CFLAGS) -fsyntax-only $(CHK_SOURCES) 36 | -------------------------------------------------------------------------------- /conductor/acrd_priv.h: -------------------------------------------------------------------------------- 1 | #ifndef __SERV_PRIV_H__ 2 | #define __SERV_PRIV_H__ 3 | 4 | #include "list.h" 5 | 6 | #define RECV_INTERVAL 1 /* ms */ 7 | #define SEND_INTERVAL 1 /* ms */ 8 | #define CPG_INTERVAL 1 /* ms */ 9 | #define SYNC_INTERVAL 20 /* ms */ 10 | 11 | #define NR_RECV_THREAD 8 12 | #define NR_SEND_THREAD 8 13 | 14 | #define MAX_MULTI_REQS 4096 15 | 16 | struct acrd_txid; 17 | struct client_info; 18 | 19 | struct acrd_op_tmpl { 20 | enum OPERATION opcode; 21 | 22 | int need_mcast; 23 | 24 | int (*exec_req)(const struct acrd_req *req, struct acrd_rsp **rsp, 25 | struct acrd_txid *txid, struct client_info *from); 26 | void (*exec_multi_reqs)(const struct acrd_req **reqs, struct acrd_rsp ***rsps, 27 | int *ret, size_t nr, struct acrd_txid *txid); 28 | void (*notify_event)(const struct acrd_req *req); 29 | }; 30 | 31 | void remove_all_watch(struct client_info *ci); 32 | 33 | int init_cpg(struct cpg_name *group_name); 34 | void cpg_handler(int fd, int events, void *arg); 35 | 36 | void do_notify_event(const struct acrd_req *req, uint16_t events, 37 | uint32_t watch_id, struct client_info *ci); 38 | int create_listen_port(int port, void *data); 39 | 40 | int init_acrd_work_queue(int in_memory); 41 | 42 | struct acrd_op_tmpl *find_op(enum OPERATION opcode); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /conductor/acrdops.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "store.h" 16 | #include "accord_proto.h" 17 | #include "logger.h" 18 | #include "util.h" 19 | #include "acrd_priv.h" 20 | #include "errno.h" 21 | 22 | struct watcher { 23 | /* watching node name */ 24 | char *path; 25 | uint32_t id; 26 | uint32_t mask; 27 | struct client_info *ci; 28 | struct list_head w_list; 29 | }; 30 | 31 | static LIST_HEAD(watchers_list); 32 | 33 | void remove_all_watch(struct client_info *ci) 34 | { 35 | struct watcher *w, *wtmp; 36 | 37 | list_for_each_entry_safe(w, wtmp, &watchers_list, w_list) { 38 | if (w->ci == ci) { 39 | free(w->path); 40 | list_del(&w->w_list); 41 | free(w); 42 | } 43 | } 44 | } 45 | 46 | static int exec_write_req(const struct acrd_req *req, struct acrd_rsp **rsp, 47 | struct acrd_txid *txid, struct client_info *from) 48 | { 49 | int ret = 0; 50 | const void *data; 51 | const char *path; 52 | uint32_t flags = req->flags; 53 | uint32_t size; 54 | const struct acrd_arg *path_arg, *data_arg; 55 | 56 | path_arg = get_arg(req, 0); 57 | data_arg = get_arg(req, 1); 58 | path = (char *)path_arg->data; 59 | data = data_arg->data; 60 | size = data_arg->size; 61 | 62 | if (likely(path && data)) 63 | ret = store_write(path, data, size, req->offset, flags, txid); 64 | else 65 | ret = ACRD_ERR_UNKNOWN; 66 | 67 | if (rsp) 68 | (*rsp)->result = ret; 69 | 70 | return ret; 71 | } 72 | 73 | static void __exec_write_multi_reqs(struct acrd_rsp ***rsps, int *ret, 74 | struct store_req_vec *vec, int nr) 75 | { 76 | int i, rc; 77 | 78 | rc = store_writev(vec, nr); 79 | 80 | for (i = 0; i < nr; i++) { 81 | ret[i] = rc; 82 | if (rsps[i]) 83 | (*rsps[i])->result = rc; 84 | } 85 | } 86 | 87 | static void exec_write_multi_reqs(const struct acrd_req **reqs, struct acrd_rsp ***rsps, 88 | int *ret, size_t nr, struct acrd_txid *txid) 89 | { 90 | int i, done; 91 | struct store_req_vec vec[MAX_MULTI_REQS]; 92 | 93 | done = 0; 94 | for (i = 0; i < nr; i++) { 95 | if (reqs[i]->offset == 0 && 96 | (reqs[i]->flags & ~ACRD_FLAG_SYNC) == ACRD_FLAG_CREATE) { 97 | vec[i].key = (char *)get_arg(reqs[i], 0)->data; 98 | vec[i].data = get_arg(reqs[i], 1)->data; 99 | vec[i].data_len = get_arg(reqs[i], 1)->size; 100 | continue; 101 | } 102 | 103 | if (done < i) 104 | __exec_write_multi_reqs(rsps + done, ret + done, 105 | vec + done, i - done); 106 | 107 | ret[i] = exec_write_req(reqs[i], rsps[i], txid, NULL); 108 | done = i + 1; 109 | } 110 | if (done < i) 111 | __exec_write_multi_reqs(rsps + done, ret + done, 112 | vec + done, i - done); 113 | } 114 | 115 | static int exec_read_req(const struct acrd_req *req, struct acrd_rsp **rsp, 116 | struct acrd_txid *txid, struct client_info *from) 117 | { 118 | int ret = 0; 119 | void *data; 120 | const char *path; 121 | uint32_t size; 122 | 123 | path = get_arg(req, 0)->data; 124 | size = req->size; 125 | 126 | if (likely(path)) 127 | ret = store_read(path, &data, &size, req->offset, txid); 128 | else 129 | ret = ACRD_ERR_UNKNOWN; 130 | 131 | if (ret == ACRD_SUCCESS) 132 | *rsp = add_arg(*rsp, data, size); 133 | 134 | (*rsp)->result = ret; 135 | 136 | return ret; 137 | } 138 | 139 | static int exec_del_req(const struct acrd_req *req, struct acrd_rsp **rsp, 140 | struct acrd_txid *txid, struct client_info *from) 141 | { 142 | int ret = 0; 143 | const char *path; 144 | 145 | path = get_arg(req, 0)->data; 146 | 147 | if (likely(path)) 148 | ret = store_del(path, txid); 149 | else 150 | ret = ACRD_ERR_UNKNOWN; 151 | 152 | if (rsp) 153 | (*rsp)->result = ret; 154 | 155 | return ret; 156 | } 157 | 158 | static int exec_atomic_inc_req(const struct acrd_req *req, struct acrd_rsp **rsp, 159 | struct acrd_txid *txid, struct client_info *from) 160 | { 161 | int ret = 0; 162 | void *data; 163 | const void *adddata; 164 | const char *path; 165 | uint32_t size; 166 | uint32_t d32, v; 167 | 168 | path = get_arg(req, 0)->data; 169 | adddata = get_arg(req, 1)->data; 170 | size = get_arg(req, 1)->size; 171 | 172 | if (size != sizeof(uint32_t)) 173 | goto err; 174 | 175 | if (likely(path)) 176 | ret = store_read(path, &data, &size, req->offset, txid); 177 | else 178 | ret = ACRD_ERR_UNKNOWN; 179 | 180 | if (ret != ACRD_SUCCESS) 181 | goto err; 182 | 183 | if (size != sizeof(uint32_t)) 184 | goto err; 185 | 186 | v = *(uint32_t *) adddata; 187 | d32 = *(uint32_t *)data; 188 | d32 += v; 189 | ret = store_write(path, &d32, size, req->offset, req->flags, txid); 190 | 191 | if (rsp) 192 | (*rsp)->result = ret; 193 | 194 | return ret; 195 | err: 196 | if (rsp) 197 | (*rsp)->result = ret; 198 | return ret; 199 | } 200 | 201 | static int exec_cmp_req(const struct acrd_req *req, struct acrd_rsp **rsp, 202 | struct acrd_txid *txid, struct client_info *from) 203 | { 204 | int ret = 0; 205 | const char *path; 206 | void *data1; 207 | const void *data2; 208 | uint32_t count1, count2; 209 | const struct acrd_arg *path_arg, *data_arg; 210 | 211 | path_arg = get_arg(req, 0); 212 | data_arg = get_arg(req, 1); 213 | path = path_arg->data; 214 | data2 = data_arg->data; 215 | count2 = data_arg->size; 216 | 217 | count1 = UINT32_MAX; /* FIXME: handle data larger than UINT32_MAX */ 218 | ret = store_read(path, &data1, &count1, 0, txid); 219 | if (ret != ACRD_SUCCESS) { 220 | dprintf("err when get p1\n"); 221 | goto cleanup; 222 | } 223 | 224 | /* if size is different, no need to compare 225 | * its contents. 226 | */ 227 | if (count1 != count2) 228 | ret = ACRD_ERR_NOTEQUAL; 229 | else if (memcmp(data1, data2, count1)) 230 | ret = ACRD_ERR_NOTEQUAL; 231 | else 232 | ret = ACRD_SUCCESS; 233 | cleanup: 234 | if (ret < 0) 235 | dprintf("err when cmp\n"); 236 | 237 | if (rsp) 238 | (*rsp)->result = ret; 239 | 240 | return ret; 241 | } 242 | 243 | static int exec_scmp_req(const struct acrd_req *req, struct acrd_rsp **rsp, 244 | struct acrd_txid *txid, struct client_info *from) 245 | { 246 | int ret = 0; 247 | const char *p1, *p2; 248 | void *buf, *d1 = NULL, *d2; 249 | uint32_t c1, c2; 250 | 251 | dprintf("scmp\n"); 252 | p1 = get_arg(req, 0)->data; 253 | p2 = get_arg(req, 1)->data; 254 | 255 | c1 = UINT32_MAX; /* FIXME: handle data larger than UINT32_MAX */ 256 | ret = store_read(p1, &buf, &c1, 0, txid); 257 | if (ret != ACRD_SUCCESS) { 258 | dprintf("err when get p1\n"); 259 | goto cleanup; 260 | } 261 | /* the content of buf can be changed when we call db_get next 262 | * time, so we need to preserve it here */ 263 | d1 = malloc(c1); 264 | memcpy(d1, buf, c1); 265 | 266 | c2 = UINT32_MAX; /* FIXME: handle data larger than UINT32_MAX */ 267 | ret = store_read(p2, &d2, &c2, 0, txid); 268 | if (ret != ACRD_SUCCESS) { 269 | dprintf("err when get p2\n"); 270 | goto cleanup; 271 | } 272 | 273 | /* if size is different, no need to compare 274 | * its contents. 275 | */ 276 | if (c1 != c2) { 277 | ret = ACRD_ERR_NOTEQUAL; 278 | goto cleanup; 279 | } 280 | 281 | if (memcmp(d1, d2, c1)) 282 | ret = ACRD_ERR_NOTEQUAL; 283 | else 284 | ret = ACRD_SUCCESS; 285 | 286 | cleanup: 287 | free(d1); 288 | if (ret < 0) 289 | eprintf("err when cmp\n"); 290 | 291 | if (rsp) 292 | (*rsp)->result = ret; 293 | 294 | return ret; 295 | } 296 | 297 | static int exec_copy_req(const struct acrd_req *req, struct acrd_rsp **rsp, 298 | struct acrd_txid *txid, struct client_info *from) 299 | { 300 | int ret = 0; 301 | const char *src, *dst; 302 | void *data; 303 | uint32_t count; 304 | 305 | src = get_arg(req, 0)->data; 306 | dst = get_arg(req, 1)->data; 307 | 308 | count = UINT32_MAX; /* FIXME: handle data larger than UINT32_MAX */ 309 | ret = store_read(src, &data, &count, 0, txid); 310 | if (ret != ACRD_SUCCESS) { 311 | dprintf("err when copy\n"); 312 | goto out; 313 | } 314 | 315 | if (!(req->flags & ACRD_FLAG_CREATE)) { 316 | /* delete existing key first */ 317 | ret = store_del(dst, txid); 318 | if (ret != ACRD_SUCCESS) { 319 | dprintf("no such key %s\n", dst); 320 | goto out; 321 | } 322 | } 323 | ret = store_write(dst, data, count, 0, ACRD_FLAG_CREATE | req->flags, txid); 324 | if (ret != ACRD_SUCCESS) 325 | dprintf("err when copy\n"); 326 | out: 327 | if (rsp) 328 | (*rsp)->result = ret; 329 | 330 | return ret; 331 | } 332 | 333 | /* 334 | * returns the size of written buffer, -1 on error 335 | */ 336 | static int acrd_tx(const struct acrd_req *req, struct acrd_rsp **rsp, 337 | struct client_info *from) 338 | { 339 | int ret; 340 | struct acrd_txid tx; 341 | struct acrd_op_tmpl *op; 342 | const struct acrd_req *child_req; 343 | const struct acrd_arg *arg; 344 | 345 | dprintf("%s\n", __func__); 346 | ret = store_tx_begin(&tx); 347 | if (ret < 0) 348 | return -1; 349 | 350 | dprintf("acrd_tx start.\n"); 351 | for_each_arg(arg, req) { 352 | child_req = (const struct acrd_req *)arg->data; 353 | op = find_op(child_req->opcode); 354 | 355 | ret = op->exec_req(child_req, rsp, &tx, from); 356 | if (ret != ACRD_SUCCESS) 357 | goto cleanup; 358 | } 359 | 360 | ret = store_tx_commit(&tx); 361 | if (ret < 0) { 362 | ret = ACRD_ERR_UNKNOWN; 363 | goto cleanup; 364 | } 365 | 366 | dprintf("commited\n"); 367 | return ret; 368 | cleanup: 369 | store_tx_abort(&tx); 370 | if (rsp) 371 | (*rsp)->data_length = 0;; 372 | 373 | dprintf("aborted. ret %d\n", ret); 374 | 375 | return ret; 376 | } 377 | 378 | static int exec_tx_req(const struct acrd_req *req, struct acrd_rsp **rsp, 379 | struct acrd_txid *txid, struct client_info *from) 380 | { 381 | int ret = 0; 382 | 383 | ret = acrd_tx(req, rsp, from); 384 | 385 | if (rsp) 386 | (*rsp)->result = ret; 387 | 388 | return ret; 389 | } 390 | 391 | static int add_file_to_list(const char *file, void *opaque) 392 | { 393 | struct acrd_rsp **rsp = opaque; 394 | 395 | *rsp = append_arg(*rsp, file, strlen(file) + 1); 396 | if (!*rsp) 397 | return ENOMEM; 398 | 399 | return 0; 400 | } 401 | 402 | static int exec_list_req(const struct acrd_req *req, struct acrd_rsp **rsp, 403 | struct acrd_txid *txid, struct client_info *from) 404 | { 405 | int ret; 406 | const char *path = NULL; 407 | 408 | if (req->data_length > 0) 409 | path = get_arg(req, 0)->data; 410 | 411 | ret = store_list(path, add_file_to_list, rsp, txid); 412 | 413 | (*rsp)->result = ret; 414 | 415 | return 0; 416 | } 417 | 418 | static int exec_add_watch_req(const struct acrd_req *req, struct acrd_rsp **rsp, 419 | struct acrd_txid *txid, struct client_info *from) 420 | { 421 | struct watcher *w = NULL; 422 | const char *path, *data; 423 | 424 | path = get_arg(req, 0)->data; 425 | data = get_arg(req, 1)->data; 426 | 427 | w = zalloc(sizeof(struct watcher)); 428 | if (w == NULL) 429 | goto failed; 430 | 431 | w->ci = from; 432 | w->path = strdup(path); 433 | w->id = req->id; 434 | memcpy(&w->mask, data, sizeof(w->mask)); 435 | dprintf("added path %s\n", w->path); 436 | 437 | list_add(&w->w_list, &watchers_list); 438 | 439 | *rsp = add_arg(*rsp, &w->id, sizeof(w->id)); 440 | (*rsp)->result = ACRD_SUCCESS; 441 | 442 | return ACRD_SUCCESS; 443 | failed: 444 | free(w); 445 | return ACRD_ERR_UNKNOWN; 446 | } 447 | 448 | static int exec_rm_watch_req(const struct acrd_req *req, struct acrd_rsp **rsp, 449 | struct acrd_txid *txid, struct client_info *from) 450 | { 451 | struct watcher *w, *wtmp; 452 | int ret = ACRD_ERR_NOTFOUND; 453 | uint32_t id; 454 | 455 | memcpy(&id, get_arg(req, 0)->data, sizeof(id)); 456 | 457 | list_for_each_entry_safe(w, wtmp, &watchers_list, w_list) { 458 | if (w->ci == from && w->id == id) { 459 | free(w->path); 460 | list_del(&w->w_list); 461 | free(w); 462 | ret = ACRD_SUCCESS; 463 | break; 464 | } 465 | } 466 | 467 | (*rsp)->result = ret; 468 | 469 | return ret; 470 | } 471 | 472 | static int cmp_watch_path(const char *path, const char *watch_path, uint32_t ev) 473 | { 474 | if (ev & ACRD_EVENT_PREFIX) 475 | return strncmp(path, watch_path, strlen(watch_path)); 476 | else 477 | return strcmp(path, watch_path); 478 | } 479 | 480 | static void notify_write_event(const struct acrd_req *req) 481 | { 482 | struct watcher *w; 483 | const char *path; 484 | 485 | path = get_arg(req, 0)->data; 486 | 487 | list_for_each_entry(w, &watchers_list, w_list) { 488 | if (cmp_watch_path(path, w->path, w->mask) == 0) { 489 | if (req->flags & ACRD_FLAG_CREATE) { 490 | dprintf("created\n"); 491 | if (w->mask & ACRD_EVENT_CREATED) 492 | do_notify_event(req, ACRD_EVENT_CREATED, 493 | w->id, w->ci); 494 | } else { 495 | dprintf("changed\n"); 496 | if (w->mask & ACRD_EVENT_CHANGED) 497 | do_notify_event(req, ACRD_EVENT_CHANGED, 498 | w->id, w->ci); 499 | } 500 | } 501 | } 502 | } 503 | 504 | static void notify_del_event(const struct acrd_req *req) 505 | { 506 | struct watcher *w; 507 | const char *path; 508 | 509 | path = get_arg(req, 0)->data; 510 | 511 | list_for_each_entry(w, &watchers_list, w_list) { 512 | if (cmp_watch_path(path, w->path, w->mask) == 0) { 513 | dprintf("deleted\n"); 514 | if (w->mask & ACRD_EVENT_DELETED) 515 | do_notify_event(req, ACRD_EVENT_DELETED, w->id, 516 | w->ci); 517 | } 518 | } 519 | } 520 | 521 | static void notify_copy_event(const struct acrd_req *req) 522 | { 523 | struct watcher *w; 524 | const char *path; 525 | 526 | path = get_arg(req, 1)->data; 527 | 528 | list_for_each_entry(w, &watchers_list, w_list) { 529 | if (cmp_watch_path(path, w->path, w->mask) == 0) { 530 | dprintf("copied\n"); 531 | if (w->mask & ACRD_EVENT_COPIED) 532 | do_notify_event(req, ACRD_EVENT_COPIED, w->id, 533 | w->ci); 534 | } 535 | } 536 | } 537 | 538 | static void notify_tx_event(const struct acrd_req *req) 539 | { 540 | struct acrd_op_tmpl *op; 541 | const struct acrd_arg *arg; 542 | const struct acrd_req *child_req; 543 | 544 | for_each_arg(arg, req) { 545 | child_req = (const struct acrd_req *)arg->data; 546 | op = find_op(child_req->opcode); 547 | if (op->notify_event) 548 | op->notify_event(child_req); 549 | } 550 | } 551 | 552 | static struct acrd_op_tmpl acrd_ops[] = { 553 | { 554 | .opcode = ACRD_OP_WRITE, 555 | .need_mcast = 1, 556 | .exec_multi_reqs = exec_write_multi_reqs, 557 | .exec_req = exec_write_req, 558 | .notify_event = notify_write_event, 559 | }, { 560 | .opcode = ACRD_OP_READ, 561 | .need_mcast = 0, 562 | .exec_req = exec_read_req, 563 | }, { 564 | .opcode = ACRD_OP_DEL, 565 | .need_mcast = 1, 566 | .exec_req = exec_del_req, 567 | .notify_event = notify_del_event, 568 | }, { 569 | .opcode = ACRD_OP_CMP, 570 | .need_mcast = 1, 571 | .exec_req = exec_cmp_req, 572 | }, { 573 | .opcode = ACRD_OP_SCMP, 574 | .need_mcast = 1, 575 | .exec_req = exec_scmp_req, 576 | }, { 577 | .opcode = ACRD_OP_COPY, 578 | .need_mcast = 1, 579 | .exec_req = exec_copy_req, 580 | .notify_event = notify_copy_event, 581 | }, { 582 | .opcode = ACRD_OP_ATOMIC_INC, 583 | .need_mcast = 1, 584 | .exec_req = exec_atomic_inc_req, 585 | }, { 586 | .opcode = ACRD_OP_TX, 587 | .need_mcast = 1, 588 | .exec_req = exec_tx_req, 589 | .notify_event = notify_tx_event, 590 | }, { 591 | .opcode = ACRD_OP_LIST, 592 | .need_mcast = 0, 593 | .exec_req = exec_list_req, 594 | }, { 595 | .opcode = ACRD_OP_ADD_WATCH, 596 | .need_mcast = 0, 597 | .exec_req = exec_add_watch_req, 598 | }, { 599 | .opcode = ACRD_OP_RM_WATCH, 600 | .need_mcast = 0, 601 | .exec_req = exec_rm_watch_req, 602 | } 603 | }; 604 | 605 | struct acrd_op_tmpl *find_op(enum OPERATION opcode) 606 | { 607 | int i; 608 | for (i = 0; i < ARRAY_SIZE(acrd_ops); i++) { 609 | if (opcode == acrd_ops[i].opcode) 610 | return acrd_ops + i; 611 | } 612 | eprintf("no such acrd_op, %d\n", opcode); 613 | abort(); 614 | } 615 | -------------------------------------------------------------------------------- /conductor/conductor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "store.h" 20 | #include "event.h" 21 | #include "net.h" 22 | #include "work.h" 23 | #include "logger.h" 24 | #include "acrd_priv.h" 25 | 26 | #define MAX_EVENT_SIZE 4096 27 | #define ACRD_DEFAULT_PORT 9090 28 | #define DEFAULT_STORE_DIR "/tmp/accord" 29 | 30 | static struct cpg_name group_name = { 7, "accord" }; 31 | 32 | static char program_name[] = "accord"; 33 | /* FIXME: default value for testing */ 34 | int keepidle = 1; 35 | int keepintvl = 1; 36 | int keepcnt = 2; 37 | 38 | static struct option const long_options[] = { 39 | /* common options */ 40 | {"port", required_argument, NULL, 'p'}, 41 | {"foreground", no_argument, NULL, 'f'}, 42 | {"debug", no_argument, NULL, 'd'}, 43 | {"help", no_argument, NULL, 'h'}, 44 | 45 | /* storage-related options */ 46 | {"mem", no_argument, NULL, 'm'}, 47 | 48 | /* keepalive-related options */ 49 | {"keeptimeout", required_argument, NULL, 't'}, 50 | {"keepintvl", required_argument, NULL, 'i'}, 51 | {"keepcnt", required_argument, NULL, 'c'}, 52 | 53 | {NULL, 0, NULL, 0}, 54 | }; 55 | 56 | static const char *short_options = "p:fl:dhmt:i:c:"; 57 | 58 | static void usage(int status) 59 | { 60 | if (status) 61 | fprintf(stderr, "Try `%s --help' for more information.\n", 62 | program_name); 63 | else { 64 | printf("Usage: %s [OPTION] [PATH]\n", program_name); 65 | printf("\ 66 | Accord Daemon\n\ 67 | -p, --port specify the listen port number\n\ 68 | -f, --foreground make the program run in the foreground\n\ 69 | -d, --debug print debug messages\n\ 70 | -m, --mem run in an in-memory mode\n\ 71 | -t, --keeptimeout specify idle time of sending keepalive packet to\ 72 | detect client failure\n\ 73 | -i, --keeptintvl specify the period of sending keepalive packet to\ 74 | detect client failure\n\ 75 | -c, --keepcnt specify the count of retrying to send packet.\ 76 | timeout value is : keeptimeout + keepintvl * keepcnt.\n\ 77 | -h, --help display this help and exit\n\ 78 | "); 79 | } 80 | exit(status); 81 | } 82 | 83 | int main(int argc, char *argv[]) 84 | { 85 | int ch, longindex; 86 | unsigned short port = ACRD_DEFAULT_PORT; 87 | int in_memory_mode = 0; 88 | int is_daemon = 1, is_debug = 0; 89 | const char *dir = DEFAULT_STORE_DIR; 90 | char logfile[PATH_MAX]; 91 | 92 | signal(SIGPIPE, SIG_IGN); 93 | 94 | while ((ch = getopt_long(argc, argv, short_options, long_options, 95 | &longindex)) >= 0) { 96 | switch (ch) { 97 | case 'p': 98 | port = atoi(optarg); 99 | break; 100 | case 'f': 101 | is_daemon = 0; 102 | break; 103 | case 'd': 104 | is_debug = 1; 105 | break; 106 | case 't': 107 | keepidle = atoi(optarg); 108 | break; 109 | case 'i': 110 | keepintvl = atoi(optarg); 111 | break; 112 | case 'c': 113 | keepcnt = atoi(optarg); 114 | break; 115 | case 'm': 116 | in_memory_mode = 1; 117 | break; 118 | case 'h': 119 | usage(0); 120 | break; 121 | default: 122 | usage(1); 123 | break; 124 | } 125 | } 126 | 127 | if (optind != argc) 128 | dir = argv[optind]; 129 | 130 | if (is_daemon && daemon(0, 0)) 131 | exit(1); 132 | 133 | strncpy(logfile, dir, sizeof(logfile)); 134 | strncat(logfile, "/acrd.log", sizeof(logfile) - strlen(logfile) - 1); 135 | if (log_init(program_name, LOG_SPACE_SIZE, is_daemon, is_debug, logfile)) 136 | exit(1); 137 | 138 | /* TODO: add error handling and parsing arguments. */ 139 | dprintf("start to init accord.\n"); 140 | if (init_event(MAX_EVENT_SIZE) < 0) { 141 | eprintf("failed to epoll.\n"); 142 | exit(1); 143 | } 144 | 145 | dprintf("init corosync.\n"); 146 | if (init_cpg(&group_name) < 0) { 147 | eprintf("failed to init corosync/cpg.\n"); 148 | exit(1); 149 | } 150 | 151 | if (init_acrd_work_queue(in_memory_mode) != 0) { 152 | eprintf("failed to init work queue.\n"); 153 | exit(1); 154 | } 155 | 156 | dprintf("initdb.\n"); 157 | if (store_init(dir, in_memory_mode) < 0) { 158 | eprintf("failed to init berkeley db." 159 | "make sure that directory permission %s is correct" 160 | "and PATH is specified in absolute path.\n", dir); 161 | exit(1); 162 | } 163 | 164 | dprintf("create listen port.\n"); 165 | if (create_listen_port(port, NULL)) { 166 | eprintf("failed to listen.\n"); 167 | exit(1); 168 | } 169 | 170 | event_loop(-1); 171 | 172 | dprintf("exit.\n"); 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /conductor/logger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | * 11 | * This code is based on log.c from Linux target framework (tgt): 12 | * Copyright (C) 2002-2003 Ardis Technolgies 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "logger.h" 33 | 34 | #define LOGDBG 0 35 | 36 | #if LOGDBG 37 | #define logdbg(file, fmt, args...) fprintf(file, fmt, ##args) 38 | #else 39 | #define logdbg(file, fmt, args...) do {} while (0) 40 | #endif 41 | 42 | static int log_enqueue(int prio, const char *fmt, va_list ap) 43 | __attribute__ ((format (printf, 2, 0))); 44 | static void dolog(int prio, const char *fmt, va_list ap) 45 | __attribute__ ((format (printf, 2, 0))); 46 | 47 | static struct logarea *la; 48 | static const char *log_name; 49 | int is_debug = 0; 50 | static pid_t pid; 51 | static key_t semkey; 52 | 53 | static int logarea_init (int size) 54 | { 55 | int shmid; 56 | 57 | logdbg(stderr,"enter logarea_init\n"); 58 | 59 | if ((shmid = shmget(IPC_PRIVATE, sizeof(struct logarea), 60 | 0644 | IPC_CREAT | IPC_EXCL)) == -1) { 61 | syslog(LOG_ERR, "shmget logarea failed %d", errno); 62 | return 1; 63 | } 64 | 65 | la = shmat(shmid, NULL, 0); 66 | if (!la) { 67 | syslog(LOG_ERR, "shmat logarea failed %d", errno); 68 | return 1; 69 | } 70 | 71 | shmctl(shmid, IPC_RMID, NULL); 72 | 73 | if (size < MAX_MSG_SIZE) 74 | size = LOG_SPACE_SIZE; 75 | 76 | if ((shmid = shmget(IPC_PRIVATE, size, 77 | 0644 | IPC_CREAT | IPC_EXCL)) == -1) { 78 | syslog(LOG_ERR, "shmget msg failed %d", errno); 79 | shmdt(la); 80 | return 1; 81 | } 82 | 83 | la->start = shmat(shmid, NULL, 0); 84 | if (!la->start) { 85 | syslog(LOG_ERR, "shmat msg failed %d", errno); 86 | shmdt(la); 87 | return 1; 88 | } 89 | memset(la->start, 0, size); 90 | 91 | shmctl(shmid, IPC_RMID, NULL); 92 | 93 | la->empty = 1; 94 | la->end = (char *)la->start + size; 95 | la->head = la->start; 96 | la->tail = la->start; 97 | 98 | if ((shmid = shmget(IPC_PRIVATE, MAX_MSG_SIZE + sizeof(struct logmsg), 99 | 0644 | IPC_CREAT | IPC_EXCL)) == -1) { 100 | syslog(LOG_ERR, "shmget logmsg failed %d", errno); 101 | shmdt(la->start); 102 | shmdt(la); 103 | return 1; 104 | } 105 | la->buff = shmat(shmid, NULL, 0); 106 | if (!la->buff) { 107 | syslog(LOG_ERR, "shmat logmsgfailed %d", errno); 108 | shmdt(la->start); 109 | shmdt(la); 110 | return 1; 111 | } 112 | 113 | shmctl(shmid, IPC_RMID, NULL); 114 | 115 | if ((la->semid = semget(semkey, 1, 0666 | IPC_CREAT)) < 0) { 116 | syslog(LOG_ERR, "semget failed %d", errno); 117 | shmdt(la->buff); 118 | shmdt(la->start); 119 | shmdt(la); 120 | return 1; 121 | } 122 | 123 | la->semarg.val=1; 124 | if (semctl(la->semid, 0, SETVAL, la->semarg) < 0) { 125 | syslog(LOG_ERR, "semctl failed %d", errno); 126 | shmdt(la->buff); 127 | shmdt(la->start); 128 | shmdt(la); 129 | return 1; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | static void free_logarea (void) 136 | { 137 | if (la->fd >= 0) 138 | close(la->fd); 139 | semctl(la->semid, 0, IPC_RMID, la->semarg); 140 | shmdt(la->buff); 141 | shmdt(la->start); 142 | shmdt(la); 143 | } 144 | 145 | #if LOGDBG 146 | static void dump_logarea (void) 147 | { 148 | struct logmsg * msg; 149 | 150 | logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n", 151 | la->start, la->end); 152 | logdbg(stderr, "|addr |next |prio|msg\n"); 153 | 154 | for (msg = (struct logmsg *)la->head; (void *)msg != la->tail; 155 | msg = msg->next) 156 | logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, 157 | msg->prio, (char *)&msg->str); 158 | 159 | logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, 160 | msg->prio, (char *)&msg->str); 161 | 162 | logdbg(stderr, "\n\n"); 163 | } 164 | #endif 165 | 166 | static int log_enqueue(int prio, const char *fmt, va_list ap) 167 | { 168 | int len, fwd; 169 | char *p, buff[MAX_MSG_SIZE]; 170 | struct logmsg *msg; 171 | struct logmsg *lastmsg; 172 | 173 | lastmsg = (struct logmsg *)la->tail; 174 | 175 | if (!la->empty) { 176 | fwd = sizeof(struct logmsg) + 177 | strlen((char *)&lastmsg->str) * sizeof(char) + 1; 178 | la->tail = (char *)la->tail + fwd; 179 | } 180 | 181 | p = buff; 182 | 183 | if (la->fd != -1) { 184 | time_t t; 185 | struct tm *tmp; 186 | 187 | t = time(NULL); 188 | tmp = localtime(&t); 189 | 190 | strftime(p, MAX_MSG_SIZE, "%b %2d %I:%M:%S ", tmp); 191 | p += strlen(p); 192 | } 193 | 194 | vsnprintf(p, MAX_MSG_SIZE - strlen(p), fmt, ap); 195 | len = strlen(buff) * sizeof(char) + 1; 196 | 197 | /* not enough space on tail : rewind */ 198 | if (la->head <= la->tail && 199 | (len + sizeof(struct logmsg)) > ((char *)la->end - (char *)la->tail)) { 200 | logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); 201 | la->tail = la->start; 202 | } 203 | 204 | /* not enough space on head : drop msg */ 205 | if (la->head > la->tail && 206 | (len + sizeof(struct logmsg)) > ((char *)la->head - (char *)la->tail)) { 207 | logdbg(stderr, "enqueue: log area overrun, drop msg\n"); 208 | 209 | if (!la->empty) 210 | la->tail = lastmsg; 211 | 212 | return 1; 213 | } 214 | 215 | /* ok, we can stage the msg in the area */ 216 | la->empty = 0; 217 | msg = (struct logmsg *)la->tail; 218 | msg->prio = prio; 219 | memcpy((void *)&msg->str, buff, len); 220 | lastmsg->next = la->tail; 221 | msg->next = la->head; 222 | 223 | logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg, msg->next, 224 | msg->prio, (char *)&msg->str); 225 | 226 | #if LOGDBG 227 | dump_logarea(); 228 | #endif 229 | return 0; 230 | } 231 | 232 | static int log_dequeue(void *buff) 233 | { 234 | struct logmsg * src = (struct logmsg *)la->head; 235 | struct logmsg * dst = (struct logmsg *)buff; 236 | struct logmsg * lst = (struct logmsg *)la->tail; 237 | int len; 238 | 239 | if (la->empty) 240 | return 1; 241 | 242 | len = strlen((char *)&src->str) * sizeof(char) + 243 | sizeof(struct logmsg) + 1; 244 | 245 | dst->prio = src->prio; 246 | memcpy(dst, src, len); 247 | 248 | if (la->tail == la->head) 249 | la->empty = 1; /* we purge the last logmsg */ 250 | else { 251 | la->head = src->next; 252 | lst->next = la->head; 253 | } 254 | logdbg(stderr, "dequeue: %p, %p, %i, %s\n", 255 | (void *)src, src->next, src->prio, (char *)&src->str); 256 | 257 | memset((void *)src, 0, len); 258 | 259 | return la->empty; 260 | } 261 | 262 | /* 263 | * this one can block under memory pressure 264 | */ 265 | static void log_syslog (void * buff) 266 | { 267 | struct logmsg * msg = (struct logmsg *)buff; 268 | 269 | if (la->fd >= 0) 270 | write(la->fd, (char *)&msg->str, strlen((char *)&msg->str)); 271 | else 272 | syslog(msg->prio, "%s", (char *)&msg->str); 273 | } 274 | 275 | static void dolog(int prio, const char *fmt, va_list ap) 276 | { 277 | struct sembuf ops; 278 | 279 | if (la) { 280 | ops.sem_num = 0; 281 | ops.sem_flg = SEM_UNDO; 282 | ops.sem_op = -1; 283 | if (semop(la->semid, &ops, 1) < 0) { 284 | syslog(LOG_ERR, "semop up failed %m"); 285 | return; 286 | } 287 | 288 | log_enqueue(prio, fmt, ap); 289 | 290 | ops.sem_op = 1; 291 | if (semop(la->semid, &ops, 1) < 0) { 292 | syslog(LOG_ERR, "semop down failed"); 293 | return; 294 | } 295 | } else { 296 | char p[MAX_MSG_SIZE]; 297 | int len; 298 | len = vsnprintf(p, sizeof(p), fmt, ap); 299 | write(STDERR_FILENO, p, len); 300 | fflush(stderr); 301 | } 302 | } 303 | 304 | void log_write(int prio, const char *fmt, ...) 305 | { 306 | va_list ap; 307 | 308 | va_start(ap, fmt); 309 | dolog(prio, fmt, ap); 310 | va_end(ap); 311 | } 312 | 313 | static void log_flush(void) 314 | { 315 | struct sembuf ops; 316 | 317 | while (!la->empty) { 318 | ops.sem_num = 0; 319 | ops.sem_flg = SEM_UNDO; 320 | ops.sem_op = -1; 321 | if (semop(la->semid, &ops, 1) < 0) { 322 | syslog(LOG_ERR, "semop up failed"); 323 | exit(1); 324 | } 325 | 326 | log_dequeue(la->buff); 327 | 328 | ops.sem_op = 1; 329 | if (semop(la->semid, &ops, 1) < 0) { 330 | syslog(LOG_ERR, "semop down failed"); 331 | exit(1); 332 | } 333 | log_syslog(la->buff); 334 | } 335 | } 336 | 337 | static void log_sigsegv(void) 338 | { 339 | eprintf("accord logger exits abnormally, pid:%d\n", getpid()); 340 | log_flush(); 341 | closelog(); 342 | free_logarea(); 343 | exit(1); 344 | } 345 | 346 | int log_init(const char *program_name, int size, int is_daemon, int debug, 347 | const char *outfile) 348 | { 349 | is_debug = debug; 350 | 351 | logdbg(stderr,"enter log_init\n"); 352 | log_name = program_name; 353 | 354 | semkey = random(); 355 | 356 | if (is_daemon) { 357 | struct sigaction sa_old; 358 | struct sigaction sa_new; 359 | int fd; 360 | 361 | if (outfile) { 362 | fd = open(outfile, O_CREAT | O_RDWR | O_APPEND, 0644); 363 | if (fd < 0) 364 | syslog(LOG_ERR, "failed to open %s\n", outfile); 365 | } else { 366 | fd = -1; 367 | openlog(log_name, 0, LOG_DAEMON); 368 | setlogmask (LOG_UPTO (LOG_DEBUG)); 369 | } 370 | 371 | if (logarea_init(size)) { 372 | syslog(LOG_ERR, "failed to initialize the logger\n"); 373 | return 1; 374 | } 375 | 376 | la->active = 1; 377 | la->fd = fd; 378 | pid = fork(); 379 | if (pid < 0) { 380 | syslog(LOG_ERR, "fail to fork the logger\n"); 381 | return 1; 382 | } else if (pid) { 383 | syslog(LOG_WARNING, 384 | "Target daemon logger with pid=%d started!\n", pid); 385 | return 0; 386 | } 387 | 388 | fd = open("/dev/null", O_RDWR); 389 | if (fd < 0) { 390 | syslog(LOG_ERR, "failed to open /dev/null: %s\n", 391 | strerror(errno)); 392 | exit(1); 393 | } 394 | 395 | dup2(fd, 0); 396 | dup2(fd, 1); 397 | dup2(fd, 2); 398 | setsid(); 399 | if (chdir("/") < 0) { 400 | syslog(LOG_ERR, "failed to chdir to '/': %s\n", 401 | strerror(errno)); 402 | exit(1); 403 | } 404 | 405 | /* flush on daemon's crash */ 406 | sa_new.sa_handler = (void*)log_sigsegv; 407 | sigemptyset(&sa_new.sa_mask); 408 | sa_new.sa_flags = 0; 409 | sigaction(SIGSEGV, &sa_new, &sa_old ); 410 | 411 | prctl(PR_SET_PDEATHSIG, SIGSEGV); 412 | 413 | while (la->active) { 414 | log_flush(); 415 | sleep(1); 416 | } 417 | 418 | exit(0); 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | void log_close(void) 425 | { 426 | if (la) { 427 | la->active = 0; 428 | waitpid(pid, NULL, 0); 429 | 430 | dprintf("accord logger stopped, pid:%d\n", pid); 431 | log_flush(); 432 | closelog(); 433 | free_logarea(); 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /conductor/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | * 11 | * This code is based on log.h from Linux target framework (tgt). 12 | * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman 13 | */ 14 | #ifndef LOGGER_H 15 | #define LOGGER_H 16 | 17 | #include 18 | #include 19 | 20 | #include "util.h" 21 | 22 | union semun { 23 | int val; 24 | struct semid_ds *buf; 25 | unsigned short int *array; 26 | struct seminfo *__buf; 27 | }; 28 | 29 | #define LOG_SPACE_SIZE (8 * 1024 * 1024) 30 | #define MAX_MSG_SIZE 128 31 | 32 | struct logmsg { 33 | short int prio; 34 | void *next; 35 | char *str; 36 | }; 37 | 38 | struct logarea { 39 | int empty; 40 | int active; 41 | void *head; 42 | void *tail; 43 | void *start; 44 | void *end; 45 | char *buff; 46 | int semid; 47 | union semun semarg; 48 | int fd; 49 | }; 50 | 51 | extern int log_init(const char *progname, int size, int daemon, int level, 52 | const char *outfile); 53 | extern void log_close (void); 54 | extern void dump_logmsg (void *); 55 | extern void log_write(int prio, const char *fmt, ...) 56 | __attribute__ ((format (printf, 2, 3))); 57 | 58 | extern int is_debug; 59 | 60 | #define eprintf(fmt, args...) \ 61 | do { \ 62 | log_write(LOG_ERR, "%s(%d) " fmt, __func__, __LINE__, ##args); \ 63 | } while (0) 64 | 65 | #define dprintf(fmt, args...) \ 66 | do { \ 67 | if (unlikely(is_debug)) \ 68 | log_write(LOG_DEBUG, "%s(%d) " fmt, __func__, __LINE__, ##args);\ 69 | } while (0) 70 | 71 | #endif /* LOG_H */ 72 | -------------------------------------------------------------------------------- /conductor/store.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "accord.h" 21 | #include "store.h" 22 | #include "logger.h" 23 | #include "util.h" 24 | 25 | static DB *dbp; 26 | static DB_ENV *envp; 27 | static DB_MPOOLFILE *mpf; 28 | 29 | #define LOG_DIR_NAME "log" 30 | #define DB_FILE_NAME "accord.db" 31 | 32 | int store_init(const char *rootdir, int in_memory_mode) 33 | { 34 | int ret; 35 | uint32_t env_flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | 36 | DB_INIT_MPOOL | DB_INIT_TXN; 37 | uint32_t flags = DB_CREATE | DB_AUTO_COMMIT | DB_DIRTY_READ; 38 | char dbpath[PATH_MAX]; 39 | char logpath[PATH_MAX]; 40 | 41 | /* TODO: decide procedure whether an in_memory_mode is specified or not */ 42 | if (rootdir[0] != '/') 43 | return -1; 44 | 45 | snprintf(dbpath, sizeof(dbpath), "%s/"DB_FILE_NAME, rootdir); 46 | snprintf(logpath, sizeof(logpath), "%s/"LOG_DIR_NAME, rootdir); 47 | 48 | /* FIXME : set permission correctly */ 49 | ret = mkdir(rootdir, 0777); 50 | if (ret != 0) { 51 | if (errno == EEXIST) 52 | ; 53 | else { 54 | eprintf("creating dir %p failed.\n", dbpath); 55 | goto failed; 56 | } 57 | } 58 | 59 | ret = mkdir(logpath, 0777); 60 | if (ret != 0) { 61 | if (errno == EEXIST) 62 | ; 63 | else { 64 | eprintf("creating logpath %p is failed.\n", logpath); 65 | goto failed; 66 | } 67 | } 68 | 69 | ret = db_env_create(&envp, 0); 70 | if (ret != 0) { 71 | eprintf("db_env_create failed.\n"); 72 | goto failed; 73 | } 74 | 75 | if (in_memory_mode) { 76 | envp->set_flags(envp, DB_LOG_IN_MEMORY, 1); 77 | /* FIXME : make this value configurable */ 78 | envp->set_cachesize(envp, 4, 1, 1); 79 | 80 | env_flags |= DB_PRIVATE; 81 | } 82 | 83 | /* FIXME : make this value configurable */ 84 | envp->set_lg_bsize(envp, 1024*1024*1024); 85 | envp->set_lk_max_locks(envp, 4096); 86 | envp->set_lk_max_objects(envp, 4096); 87 | envp->set_lk_max_lockers(envp, 4096); 88 | envp->set_lk_partitions(envp, 1024); 89 | ret = envp->open(envp, logpath, env_flags, 0); 90 | if (ret != 0) { 91 | eprintf("DB_ENV->open %p failed.\n", logpath); 92 | goto failed; 93 | } 94 | 95 | /* setup a DB */ 96 | ret = db_create(&dbp, envp, 0); 97 | if (ret != 0) { 98 | eprintf("%s\n", db_strerror(ret)); 99 | return -1; 100 | } 101 | 102 | dbp->set_pagesize(dbp, 65536); 103 | ret = dbp->set_flags(dbp, DB_TXN_NOT_DURABLE); 104 | if (in_memory_mode) { 105 | mpf = dbp->get_mpf(dbp); 106 | mpf->set_flags(mpf, DB_MPOOL_NOFILE, 1); 107 | } 108 | 109 | if (ret != 0) { 110 | eprintf("db_set_flags failed.\n"); 111 | goto failed; 112 | } 113 | 114 | if (in_memory_mode) 115 | ret = dbp->open(dbp, NULL, NULL, NULL, DB_BTREE, flags, 0664); 116 | else 117 | ret = dbp->open(dbp, NULL, dbpath, NULL, DB_BTREE, flags, 0664); 118 | if (ret != 0) { 119 | eprintf("db_open failed.\n"); 120 | goto failed; 121 | } 122 | 123 | return 0; 124 | failed: 125 | dbp->err(dbp, ret, "%s", dbpath); 126 | dbp->close(dbp, 0); 127 | return -1; 128 | } 129 | 130 | int store_read(const char *key, void **data, uint32_t *data_len, 131 | uint64_t offset, struct acrd_txid *txid) 132 | { 133 | DBT db_key, db_data; 134 | int ret; 135 | DB_TXN *tid = NULL; 136 | 137 | if (txid) 138 | tid = txid->tid; 139 | 140 | memset(&db_key, 0, sizeof(db_key)); 141 | memset(&db_data, 0, sizeof(db_data)); 142 | 143 | db_key.data = (void *)key; 144 | db_key.size = strlen(key) + 1; 145 | 146 | db_data.doff = offset; 147 | db_data.dlen = *data_len; 148 | db_data.flags = DB_DBT_PARTIAL; 149 | 150 | ret = dbp->get(dbp, tid, &db_key, &db_data, DB_DIRTY_READ); 151 | if (ret != 0) { 152 | switch (ret) { 153 | case DB_NOTFOUND: 154 | return ACRD_ERR_NOTFOUND; 155 | default: 156 | dbp->err(dbp, ret, "DB->get"); 157 | return ACRD_ERR_UNKNOWN; 158 | } 159 | } 160 | 161 | if (data) 162 | *data = db_data.data; 163 | 164 | *data_len = db_data.size; 165 | 166 | return ACRD_SUCCESS; 167 | } 168 | 169 | static int store_exists(const char *key, struct acrd_txid *txid) 170 | { 171 | DBT db_key; 172 | int ret; 173 | DB_TXN *tid = NULL; 174 | 175 | if (txid) 176 | tid = txid->tid; 177 | 178 | memset(&db_key, 0, sizeof(db_key)); 179 | 180 | db_key.data = (void *)key; 181 | db_key.size = strlen(key) + 1; 182 | 183 | ret = dbp->exists(dbp, tid, &db_key, 0); 184 | if (ret == 0) 185 | return 1; 186 | if (ret == DB_NOTFOUND) 187 | return 0; 188 | else { 189 | dbp->err(dbp, ret, "DB->exists"); 190 | return 0; 191 | } 192 | } 193 | 194 | int store_write(const char *key, const void *data, uint32_t data_len, 195 | uint64_t offset, uint32_t flags, struct acrd_txid *txid) 196 | { 197 | DBT db_key, db_data, db_tmp; 198 | int ret; 199 | uint32_t db_flags = 0; 200 | DB_TXN *tid = NULL; 201 | 202 | if (flags & ACRD_FLAG_CREATE) { 203 | if (flags & ACRD_FLAG_EXCL) 204 | db_flags = DB_NOOVERWRITE; 205 | } else if (!store_exists(key, txid)) 206 | return ACRD_ERR_NOTFOUND; 207 | 208 | if (txid) 209 | tid = txid->tid; 210 | 211 | memset(&db_key, 0, sizeof(db_key)); 212 | memset(&db_data, 0, sizeof(db_data)); 213 | memset(&db_tmp, 0, sizeof(db_data)); 214 | 215 | db_key.data = (void *)key; 216 | db_key.size = strlen(key) + 1; 217 | 218 | db_data.data = (void *)data; 219 | db_data.doff = offset; 220 | db_data.dlen = data_len; 221 | db_data.size = data_len; 222 | db_data.flags = DB_DBT_PARTIAL; 223 | 224 | if (flags & ACRD_FLAG_APPEND) { 225 | ret = dbp->get(dbp, tid, &db_key, &db_tmp, 0); 226 | if (ret != DB_NOTFOUND && ret != 0) { 227 | dbp->err(dbp, ret, "DB->get"); 228 | return ACRD_ERR_UNKNOWN; 229 | } 230 | 231 | db_data.doff = db_tmp.size; 232 | } 233 | 234 | ret = dbp->put(dbp, tid, &db_key, &db_data, db_flags); 235 | if (ret != 0) { 236 | switch (ret) { 237 | case DB_KEYEXIST: 238 | return ACRD_ERR_EXIST; 239 | default: 240 | dbp->err(dbp, ret, "DB->put"); 241 | return ACRD_ERR_UNKNOWN; 242 | } 243 | } 244 | 245 | return ACRD_SUCCESS; 246 | } 247 | 248 | int store_writev(struct store_req_vec *vec, int nr) 249 | { 250 | DBT db_key; 251 | int ret; 252 | size_t buf_size = 0; 253 | static char *buf; 254 | void *opaque; 255 | int i; 256 | 257 | for (i = 0; i < nr; i++) 258 | buf_size += strlen(vec[i].key) + vec[i].data_len + 256; 259 | 260 | buf = malloc(buf_size); 261 | 262 | memset(&db_key, 0, sizeof(db_key)); 263 | 264 | db_key.ulen = buf_size; 265 | db_key.data = buf; 266 | db_key.flags = DB_DBT_USERMEM; 267 | 268 | DB_MULTIPLE_WRITE_INIT(opaque, &db_key); 269 | 270 | for (i = 0; i < nr; i++) 271 | DB_MULTIPLE_KEY_WRITE_NEXT(opaque, &db_key, vec[i].key, 272 | strlen(vec[i].key) + 1, vec[i].data, 273 | vec[i].data_len); 274 | 275 | ret = dbp->put(dbp, NULL, &db_key, NULL, DB_MULTIPLE_KEY); 276 | if (ret != 0) { 277 | switch (ret) { 278 | default: 279 | dbp->err(dbp, ret, "DB->put"); 280 | return ACRD_ERR_UNKNOWN; 281 | } 282 | } 283 | 284 | free(buf); 285 | 286 | return ACRD_SUCCESS; 287 | } 288 | 289 | int store_sync(void) 290 | { 291 | return envp->memp_sync(envp, 0); 292 | } 293 | 294 | int store_del(const char *key, struct acrd_txid *txid) 295 | { 296 | DBT db_key; 297 | int ret; 298 | DB_TXN *tid = NULL; 299 | 300 | if (txid) 301 | tid = txid->tid; 302 | 303 | memset(&db_key, 0, sizeof(db_key)); 304 | 305 | db_key.data = (void *)key; 306 | db_key.size = strlen(key) + 1; 307 | 308 | ret = dbp->del(dbp, tid, &db_key, 0); 309 | if (ret != 0) { 310 | switch (ret) { 311 | case DB_NOTFOUND: 312 | return ACRD_ERR_NOTFOUND; 313 | default: 314 | dbp->err(dbp, ret, "DB->del"); 315 | return ACRD_ERR_UNKNOWN; 316 | } 317 | } 318 | 319 | return ACRD_SUCCESS; 320 | } 321 | 322 | int store_list(const char *key, int (*add_file)(const char *, void *), 323 | void *opaque, struct acrd_txid *txid) 324 | { 325 | DBT db_key, db_data; 326 | int ret; 327 | DB_TXN *tid = NULL; 328 | DBC *cursor; 329 | 330 | if (txid) 331 | tid = txid->tid; 332 | 333 | ret = dbp->cursor(dbp, tid, &cursor, DB_DIRTY_READ); 334 | if (ret) { 335 | eprintf("failed open a cursor\n"); 336 | return ACRD_ERR_UNKNOWN; 337 | } 338 | 339 | memset(&db_key, 0, sizeof(db_key)); 340 | memset(&db_data, 0, sizeof(db_data)); 341 | 342 | if (key) { 343 | db_key.data = (void *)key; 344 | db_key.size = strlen(key) + 1; 345 | 346 | ret = cursor->c_get(cursor, &db_key, &db_data, DB_SET_RANGE); 347 | } else 348 | ret = cursor->c_get(cursor, &db_key, &db_data, DB_FIRST); 349 | 350 | while (ret == 0) { 351 | if (key && strncmp(db_key.data, key, strlen(key)) != 0) 352 | /* prefix doesn't match */ 353 | break; 354 | 355 | ret = add_file(db_key.data, opaque); 356 | if (ret != 0) 357 | break; 358 | 359 | ret = cursor->c_get(cursor, &db_key, &db_data, DB_NEXT); 360 | }; 361 | 362 | if (ret == 0 || ret == DB_NOTFOUND) 363 | ret = ACRD_SUCCESS; 364 | else { 365 | envp->err(envp, ret, "store_list failed\n"); 366 | ret = ACRD_ERR_UNKNOWN; 367 | } 368 | 369 | cursor->close(cursor); 370 | 371 | return ret; 372 | } 373 | 374 | int store_tx_begin(struct acrd_txid *tx) 375 | { 376 | uint32_t flags = DB_TXN_NOSYNC | DB_DIRTY_READ; 377 | int ret; 378 | DB_TXN *tid; 379 | 380 | ret = envp->txn_begin(envp, NULL, &tid, flags); 381 | if (ret != 0) { 382 | envp->err(envp, ret, "tx_begin failed\n"); 383 | return -1; 384 | } 385 | 386 | tx->tid = tid; 387 | dprintf("tid : %p\n", tid); 388 | 389 | return ret; 390 | } 391 | 392 | int store_tx_commit(struct acrd_txid *tx) 393 | { 394 | int ret; 395 | DB_TXN *tid = tx->tid; 396 | 397 | dprintf("tid : %p\n", tid); 398 | ret = tid->commit(tid, 0); 399 | 400 | if (ret != 0) { 401 | envp->err(envp, ret, "DB_TXN->commit failed\n"); 402 | return -1; 403 | } 404 | 405 | return 0; 406 | } 407 | 408 | int store_tx_abort(struct acrd_txid *tx) 409 | { 410 | int ret; 411 | DB_TXN *tid = tx->tid; 412 | 413 | dprintf("tid : %p\n", tid); 414 | ret = tid->abort(tid); 415 | if (ret != 0) { 416 | envp->err(envp, ret, "DB_TXN->abort failed\n"); 417 | eprintf("DB_TXN->abort failed\n"); 418 | return -1; 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | int store_close(void) 425 | { 426 | dbp->close(dbp, 0); 427 | envp->close(envp, 0); 428 | 429 | return 0; 430 | } 431 | -------------------------------------------------------------------------------- /conductor/store.h: -------------------------------------------------------------------------------- 1 | #ifndef __STORE_H__ 2 | #define __STORE_H__ 3 | 4 | #include 5 | 6 | struct acrd_txid { 7 | DB_TXN *tid; 8 | }; 9 | 10 | struct store_req_vec { 11 | char *key; 12 | const void *data; 13 | uint32_t data_len; 14 | }; 15 | 16 | int store_init(const char *rootdir, int in_memory_mode); 17 | int store_read(const char *key, void **data, uint32_t *data_len, 18 | uint64_t offset, struct acrd_txid *txid); 19 | int store_writev(struct store_req_vec *vec, int nr); 20 | int store_write(const char *key, const void *data, uint32_t data_len, 21 | uint64_t offset, uint32_t flags, struct acrd_txid *txid); 22 | int store_sync(void); 23 | int store_del(const char *key, struct acrd_txid *txid); 24 | int store_list(const char *key, int (*add_file)(const char *, void *), 25 | void *opaque, struct acrd_txid *txid); 26 | int store_tx_begin(struct acrd_txid *tx); 27 | int store_tx_commit(struct acrd_txid *tx); 28 | int store_tx_abort(struct acrd_txid *tx); 29 | int store_close(void); 30 | #endif /* __STORE_H__*/ 31 | -------------------------------------------------------------------------------- /include/Makefile: -------------------------------------------------------------------------------- 1 | includedir ?= $(PREFIX)/include 2 | 3 | HEADERS = accord.h accord_proto.h 4 | 5 | .PHONY:all 6 | all: 7 | 8 | .PHONY:install 9 | install: $(HEADERS) 10 | install -d -m 755 $(DESTDIR)$(includedir) 11 | install -m 644 $(HEADERS) $(DESTDIR)$(includedir) 12 | -------------------------------------------------------------------------------- /include/accord.h: -------------------------------------------------------------------------------- 1 | #ifndef __ACCORD_H__ 2 | #define __ACCORD_H__ 3 | 4 | #include 5 | 6 | struct acrd_handle; 7 | struct acrd_tx; 8 | struct acrd_watch_info; 9 | struct acrd_aiocb; 10 | 11 | typedef void (*acrd_watch_cb_t)(struct acrd_handle *ah, 12 | struct acrd_watch_info *info, void *arg); 13 | 14 | 15 | /** 16 | * Callback function definition when a client joined/left. 17 | * Argument description is as follows : 18 | * 19 | * 'member_list' Array of connecting clients id. 20 | * 'member_list_entries' A size of array member_list. 21 | * 'nodeid' : A client id of left/joined node. 22 | * It is assured that the first callback of 23 | * join_cb from servers contains assigned nodeid from 24 | * Accord servers. 25 | * 'arg' An argument pointer which is set when acrd_init() is called. 26 | * return value is handler to call the other Accord APIs. 27 | */ 28 | typedef void (*acrd_confchg_cb_t)(struct acrd_handle *ah, 29 | const uint64_t *member_list, 30 | size_t member_list_entries, uint64_t nodeid, 31 | void *arg); 32 | 33 | 34 | 35 | typedef void (*acrd_aio_cb_t)(struct acrd_handle *ah, struct acrd_aiocb *aiocb, 36 | void *arg); 37 | typedef void (*acrd_list_cb_t)(struct acrd_handle *ah, const char *path, 38 | void *arg); 39 | 40 | struct acrd_watch_info { 41 | struct acrd_handle *handle; 42 | const char *path; /* path the event occurs*/ 43 | const void *data; 44 | unsigned int data_len; 45 | uint64_t offset; /* The changed file location */ 46 | void *ctx; 47 | uint16_t events; /* An occured event number */ 48 | acrd_watch_cb_t cb; 49 | uint32_t id; 50 | uint32_t mask; 51 | }; 52 | 53 | struct acrd_aiocb { 54 | struct acrd_handle *handle; 55 | int done; 56 | uint32_t result; 57 | acrd_aio_cb_t cb; 58 | void *arg; 59 | }; 60 | 61 | struct acrd_listcb { 62 | acrd_list_cb_t cb; 63 | void *arg; 64 | }; 65 | 66 | /** 67 | * Create a new connection to the Accord servers 68 | * 69 | * This function is used to initialize a connection to the Accord 70 | * service. Each application may have several connections to the 71 | * Accord. This function returns a handle to uniquely identify the 72 | * connection. The handle is used in other function calls to identify 73 | * the connection to be used for communication with Accord. 74 | * 75 | * Every time other clients join to or leave from Accord, the 76 | * specified callback function, join_cb or leave_cb, is called. 77 | * 78 | * Returns a created handle on success, NULL on error, 79 | * See also : acrd_confchg_cb_t 80 | */ 81 | struct acrd_handle *acrd_init(const char *hostname, int port, 82 | acrd_confchg_cb_t join_cb, acrd_confchg_cb_t leave_cb, 83 | void *arg); 84 | 85 | /** 86 | * Terminate a connection to the Accord servers 87 | * 88 | * This function closes a Accord handle and free up any resources. 89 | * Once the connection is closed, the handle may not be used again by 90 | * applications. No more callbacks will be called after this function 91 | * is called. 92 | * 93 | * Returns zero on success, -1 on error. 94 | */ 95 | int acrd_close(struct acrd_handle *handle); 96 | 97 | 98 | /* Accord I/O API */ 99 | 100 | /** 101 | * Write data to Accord 102 | * 103 | * This writes up to 'count' bytes from the buffer starting at 'data'. 104 | * The destination to save is a file 'path', 'offset' bytes. 105 | * This function blocks until the operation has been completed. 106 | * 107 | * Supported flags are as follows: 108 | * 109 | * ACRD_FLAG_CREATE 110 | * Create a new file on Accord. If a path has already exists, return 111 | * ACRD_ERR_EXIST. 112 | * 113 | * ACRD_FLAG_EXCL 114 | * Writes the file wether the file is exist or not. 115 | * 116 | * ACRD_FLAG_APPEND 117 | * If set, the file offset will be set to the end of the file prior to each write. 118 | * 119 | * ACRD_FLAG_SYNC 120 | * Writes IO is reported as completed after it has been flushed to the disks. 121 | * If NOT set ACRD_FLAG_SYNC, a response is returned when all servers are assured to 122 | * receive the write request. 123 | * 124 | * Returns ACRD_SUCCESS on success, ACRD_ERR_NOTFOUND if the specified 125 | * path does not exist but ACRD_FLAG_CREATE is not set to flags, 126 | * ACRD_ERR_EXIST if the specified path exists bug ACRD_FLAG_CREATE is 127 | * set to flags. 128 | * 129 | */ 130 | int acrd_write(struct acrd_handle *h, const char *path, const void *data, 131 | uint32_t count, uint64_t offset, uint32_t flags); 132 | 133 | /** 134 | * Read data from Accord 135 | * 136 | * The source to read is a file 'path', 'offset' bytes. 137 | * 138 | * This function blocks until the operation has been completed. 139 | * 140 | * Returns ACRD_SUCCESS on success, ACRD_ERR_NOTFOUND if the specified 141 | * path is not found. 142 | */ 143 | int acrd_read(struct acrd_handle *h, const char *path, void *data, 144 | uint32_t *count, uint64_t offset, uint32_t flags); 145 | 146 | /** 147 | * Delete the specified data from Accord 148 | * 149 | * This function blocks until the operation has been completed. 150 | * 151 | * Returns ACRD_SUCCESS on success, ACRD_ERR_NOTFOUND if the specified 152 | * path is not found. 153 | */ 154 | int acrd_del(struct acrd_handle *h, const char *path, uint32_t flags); 155 | 156 | /** 157 | * Do a prefix search and list matching keys 158 | * 159 | * This function blocks until the operation has been completed. 160 | * 161 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 162 | */ 163 | int acrd_list(struct acrd_handle *h, const char *prefix, uint32_t flags, 164 | struct acrd_listcb *listcb); 165 | 166 | /** 167 | * Copy data on the Accord server 168 | * 169 | * This function blocks until the operation has been completed. 170 | * 171 | * On success, ACRD_SUCCESS is returned. If the src path is not found, 172 | * or the dst path does not exist but ACRD_FLAG_CREATE is not set to 173 | * flags, ACRD_ERR_NOTFOUND is returned. Returns ACRD_ERR_EXIST if the 174 | * dst path exists bug ACRD_FLAG_CREATE is set to flags 175 | */ 176 | int acrd_copy(struct acrd_handle *h, const char *src, const char *dst, 177 | uint32_t flags); 178 | 179 | 180 | /* Accord asynchronous I/O API */ 181 | 182 | /** 183 | * Create an asynchronous I/O control block 184 | * 185 | * This function creates an asynchronous I/O control block for the 186 | * asyncronous operation. Every AIO operations needs the aiocb 187 | * returned by this function. Each AIO operation is identified with 188 | * the aiocb. If you want to wait one of the AIO operations, call 189 | * acrd_aio_wait() with its aiocb. 190 | * 191 | * Returns a created aiocb on success, NULL on error. 192 | */ 193 | struct acrd_aiocb *acrd_aio_setup(struct acrd_handle *h, acrd_aio_cb_t cb, 194 | void *arg); 195 | 196 | /** 197 | * Release an asynchronous I/O control block 198 | * 199 | * This function free up any resources used by the aiocb. This may 200 | * not be called before the AIO operation is finished. To ensure that 201 | * the operation is done, call acrd_aio_wait(). 202 | */ 203 | void acrd_aio_release(struct acrd_handle *h, struct acrd_aiocb *aiocb); 204 | 205 | /** 206 | * Wait for the AIO operation to be completed 207 | * 208 | * This function will wait until the specified AIO operation has 209 | * completed. 210 | */ 211 | void acrd_aio_wait(struct acrd_handle *h, struct acrd_aiocb *aiocb); 212 | 213 | /** 214 | * Flush AIO operations 215 | * 216 | * This function flushes all pending AIO operations. This will block 217 | * until all outstanding AIO operations have been completed. 218 | */ 219 | void acrd_aio_flush(struct acrd_handle *h); 220 | 221 | /** 222 | * Write data to Accord asynchronously 223 | * 224 | * This is the asynchronous version of acrd_write(). This call returns 225 | * as soon as the request has been enqueued. 226 | * 227 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 228 | * See also : acrd_write() 229 | */ 230 | int acrd_aio_write(struct acrd_handle *h, const char *path, const void *data, 231 | uint32_t count, uint64_t offset, uint32_t flags, 232 | struct acrd_aiocb *aiocb); 233 | 234 | /** 235 | * Read data from Accord asynchronously 236 | * 237 | * This is the asynchronous version of acrd_read(). This call returns 238 | * as soon as the request has been enqueued. 239 | * 240 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 241 | * See also : acrd_read() 242 | */ 243 | int acrd_aio_read(struct acrd_handle *h, const char *path, void *data, 244 | uint32_t *count, uint64_t offset, uint32_t flags, 245 | struct acrd_aiocb *aiocb); 246 | 247 | /** 248 | * Delete the specified data from Accord asynchronously 249 | * 250 | * This is the asynchronous version of acrd_del(). This call returns 251 | * as soon as the request has been enqueued. 252 | * 253 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 254 | */ 255 | int acrd_aio_del(struct acrd_handle *h, const char *path, uint32_t flags, 256 | struct acrd_aiocb *aiocb); 257 | 258 | /** 259 | * Do a prefix search and list matching keys asynchronously 260 | * 261 | * This is the asynchronous version of acrd_list(). This call returns 262 | * as soon as the request has been enqueued. 263 | * 264 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 265 | */ 266 | int acrd_aio_list(struct acrd_handle *h, const char *prefix, uint32_t flags, 267 | struct acrd_listcb *listcb, struct acrd_aiocb *aiocb); 268 | 269 | /** 270 | * Copy data on the Accord server asynchronously 271 | * 272 | * This is the asynchronous version of acrd_copy(). This call returns 273 | * as soon as the request has been enqueued. 274 | * 275 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 276 | */ 277 | int acrd_aio_copy(struct acrd_handle *h, const char *src, const char *dst, 278 | uint32_t flags, struct acrd_aiocb *aiocb); 279 | 280 | 281 | /* Accord transaction API */ 282 | 283 | /** 284 | * Create a new transaction 285 | * 286 | * This function allocate transaction descriptor acrd_tx. 287 | * Note that acrd_tx_init() function doesn't start transaction 288 | * but just allocate memory for a new transaction. 289 | * 290 | * Returns a created acrd_tx on success, NULL on error. 291 | */ 292 | struct acrd_tx *acrd_tx_init(struct acrd_handle *h); 293 | 294 | /** 295 | * Close a transaction 296 | * 297 | * This function closes a transaction and free up any resources. 298 | */ 299 | void acrd_tx_close(struct acrd_tx *tx); 300 | 301 | /** 302 | * Add a write operation to the transaction 303 | * 304 | * This function is similar to acrd_write() but only used for a 305 | * transaction. If the operation fails, the transaction will be 306 | * aborted. This call returns as soon as the request has been added 307 | * to the transaction. 308 | * 309 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 310 | */ 311 | int acrd_tx_write(struct acrd_tx *tx, const char *path, const void *buf, 312 | uint32_t count, uint64_t offset, uint32_t flags); 313 | 314 | /** 315 | * Add a read operation to the transaction 316 | * 317 | * This function is similar to acrd_read() but only used for a 318 | * transaction. If the operation fails, the transaction will be 319 | * aborted. This call returns as soon as the request has been added 320 | * to the transaction. 321 | * 322 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 323 | */ 324 | int acrd_tx_read(struct acrd_tx *tx, const char *path, void *buf, uint32_t *count, 325 | uint64_t offset, uint32_t flags); 326 | 327 | /** 328 | * Add a delete operation to the transaction 329 | * 330 | * This function is similar to acrd_del() but only used for a 331 | * transaction. If the operation fails, the transaction will be 332 | * aborted. This call returns as soon as the request has been added 333 | * to the transaction. 334 | * 335 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 336 | */ 337 | int acrd_tx_del(struct acrd_tx *tx, const char *path, uint32_t flags); 338 | 339 | /** 340 | * Add a compare operation to the transaction 341 | * 342 | * This add a operation to compare data to a transaction. If the 343 | * content of 'path' doesn't equal to the first 'count' bytes of 344 | * 'buf', the transaction will be aborted. This call returns as soon 345 | * as the request has been added to the transaction. 346 | * 347 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 348 | */ 349 | int acrd_tx_cmp(struct acrd_tx *tx, const char *path, const void *buf, 350 | uint32_t count, uint32_t flags); 351 | 352 | /** 353 | * Add a server-side compare operation to the transaction 354 | * 355 | * This add a operation to compare data to a transaction. If the 356 | * content of 'path1' doesn't equal to the one of 'path2', the 357 | * transaction will be aborted. This call returns as soon as the 358 | * request has been added to the transaction. 359 | * 360 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 361 | */ 362 | int acrd_tx_scmp(struct acrd_tx *tx, const char *path1, const char *path2, 363 | uint32_t flags); 364 | 365 | /** 366 | * Add a copy operation to the transaction 367 | * 368 | * This function is similar to acrd_copy() but only used for a 369 | * transaction. If the operation fails, the transaction will be 370 | * aborted. This call returns as soon as the request has been added 371 | * to the transaction. 372 | * 373 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 374 | */ 375 | int acrd_tx_copy(struct acrd_tx *tx, const char *src, const char *dst, 376 | uint32_t flags); 377 | 378 | int acrd_tx_atomic_inc(struct acrd_tx *tx, const char *path, const void *buf, 379 | uint32_t count, uint32_t offset, uint32_t flags); 380 | 381 | /** 382 | * Commit a transaction 383 | * 384 | * This function commits a transaction. The maximum number of 385 | * operations which can be commited at once is 65535. 386 | * 387 | * If the transaction is commited, this returns ACRD_SUCCESS. If the 388 | * transaction is aborted, this returns the error code(abort reason). 389 | * Transaction related errors is as follows: 390 | * 391 | * acrd_tx_cmp() abort : ACRD_ERR_EXIST 392 | * acrd_tx_write() abort : ACRD_ERR_NOTFOUND/ACRD_ERR_EXIST/ACRD_ERR_STORE 393 | */ 394 | int acrd_tx_commit(struct acrd_tx *tx, uint32_t flags); 395 | 396 | /** 397 | * Commit a transaction asynchronously 398 | * 399 | * This is the asynchronous version of acrd_tx_commit(). This function 400 | * returns as soon as the request has been enqueued. 401 | * 402 | * Returns ACRD_SUCCESS on success, ACRD_ERR_UNKNOWN on error. 403 | * 404 | * See also : acrd_aio_setup(), acrd_tx_commit() 405 | */ 406 | int acrd_tx_aio_commit(struct acrd_tx *tx, uint32_t flags, 407 | struct acrd_aiocb *aiocb); 408 | 409 | 410 | /* Accord watch API */ 411 | 412 | /** 413 | * Start monitoring data on the Accord server 414 | * 415 | * This starts monitoring data change events. The events to be 416 | * monitored for 'path' are specified in the 'mask' bit-mask argument. 417 | * The following bits can be specified in 'mask': 418 | * 419 | * ACRD_EVENT_CREATED Data is created 420 | * ACRD_EVENT_DELETED Data is deleted 421 | * ACRD_EVENT_CHANGED Data is changed 422 | * ACRD_EVENT_COPIED Data is copied 423 | * ACRD_EVENT_ALL A bit mask of all of the above events 424 | * 425 | * If you set EVENT_PREFIX flag to the mask, you can watch multiple 426 | * files which start with 'path'. 427 | * 428 | * This returns acrd_watch_info on success, NULL on error. 429 | */ 430 | struct acrd_watch_info *acrd_add_watch(struct acrd_handle *h, const char *path, 431 | uint32_t mask, acrd_watch_cb_t cb, 432 | void *arg); 433 | 434 | /** 435 | * Stop monitoring data on the Accord server 436 | * 437 | * This function removes the registered acrd_watch_info from ths Accord 438 | * server. 439 | * 440 | * Returns ACRD_SUCCESS on success, ACRD_ERR_NOTFOUND if the specified 441 | * acrd_watch_info is not registerd on the Accord server. 442 | */ 443 | int acrd_rm_watch(struct acrd_handle *h, struct acrd_watch_info *bw); 444 | 445 | #endif /* __ACCORD_H__ */ 446 | -------------------------------------------------------------------------------- /include/accord_proto.h: -------------------------------------------------------------------------------- 1 | #ifndef __ACCORD_PROTO_H__ 2 | #define __ACCORD_PROTO_H__ 3 | 4 | #include "stdint.h" 5 | #include "util.h" 6 | 7 | enum TYPE { 8 | ACRD_MSG_REQUEST = 0x00, 9 | ACRD_MSG_RESPONSE, 10 | ACRD_MSG_NOTIFICATION 11 | }; 12 | 13 | enum OPERATION { 14 | ACRD_OP_WRITE = 0x00, 15 | ACRD_OP_READ, 16 | ACRD_OP_DEL, 17 | ACRD_OP_TX, 18 | ACRD_OP_CMP, 19 | ACRD_OP_SCMP, 20 | ACRD_OP_COPY, 21 | ACRD_OP_ATOMIC_INC, 22 | ACRD_OP_LIST, 23 | ACRD_OP_ADD_WATCH, 24 | ACRD_OP_RM_WATCH, 25 | ACRD_OP_INVALID /* this op shouldn't be called */ 26 | }; 27 | 28 | enum RESULT { 29 | ACRD_SUCCESS = 0, 30 | 31 | /* unknown */ 32 | ACRD_ERR_UNKNOWN = 0x10, 33 | 34 | /* common error */ 35 | ACRD_ERR_AGAIN = 0x20, 36 | 37 | /* tcp connection error */ 38 | ACRD_ERR_CONN = 0x30, 39 | ACRD_ERR_CONN_TIMEOUT, 40 | ACRD_ERR_CONN_REFUSED, 41 | 42 | /* msg related error */ 43 | ACRD_ERR_MSG = 0x40, 44 | ACRD_ERR_MSG_HDR, 45 | 46 | /* bdb related error */ 47 | ACRD_ERR_STORE = 0x50, 48 | ACRD_ERR_EXIST, 49 | ACRD_ERR_NOTFOUND, 50 | ACRD_ERR_NOTEQUAL, 51 | }; 52 | 53 | 54 | #define ACRD_EVENT_NONE 0x00000000 55 | 56 | #define ACRD_EVENT_CREATED 0x00000001 57 | #define ACRD_EVENT_DELETED 0x00000002 58 | #define ACRD_EVENT_CHANGED 0x00000004 59 | #define ACRD_EVENT_COPIED 0x00000008 60 | #define ACRD_EVENT_ALL 0x000000FF 61 | #define ACRD_EVENT_WATCH_MASK 0x000000FF 62 | 63 | #define ACRD_EVENT_JOINED 0x00000100 64 | #define ACRD_EVENT_LEFT 0x00000200 65 | #define ACRD_EVENT_CONFCHG_MASK 0x0000FF00 66 | 67 | #define ACRD_EVENT_PREFIX 0x01000000 68 | 69 | 70 | #define ACRD_FLAG_SYNC 0x0001 /* write IO is reported as completed, 71 | * after it has been flushed to the disks. */ 72 | #define ACRD_FLAG_CREATE 0x0002 73 | #define ACRD_FLAG_APPEND 0x0004 74 | #define ACRD_FLAG_EXCL 0x0008 75 | 76 | /** 77 | * operation arguments 78 | * PUT : path, data 79 | * GET : path 80 | * DEL : path 81 | * TX : acrd_msg 82 | */ 83 | 84 | 85 | /** 86 | * msg format : 87 | * acrd_msg |hdr| 88 | * arg1 |size|data| 89 | * arg2 |size|data| 90 | * arg3 |size|data| 91 | * 92 | * Sample : normal ops case 93 | * write(fd, hdr, sizeof(acrd_msg)); 94 | * write(fd, arg1->size, sizeof(uint32_t)); 95 | * write(fd, arg1->data, sizeof(arg1->size)); 96 | * write(fd, arg2->size, sizeof(uint32_t)); 97 | * write(fd, arg2->data, sizeof(arg2->size)); 98 | * ... 99 | * Sample2 : transaction case 100 | * write(fd, hdr, sizeof(acrd_msg)); 101 | * write(fd, hdr1, sizeof(acrd_msg)); 102 | * write(fd, hdr2, sizeof(acrd_msg)); 103 | * write(fd, arg1->size, sizeof(uint32_t)); 104 | * write(fd, arg1->data, sizeof(arg1->size)); 105 | * write(fd, arg2->size, sizeof(uint32_t)); 106 | * write(fd, arg2->data, sizeof(arg2->size)); 107 | * 108 | */ 109 | 110 | struct acrd_common_hdr { 111 | uint8_t proto_ver; 112 | uint8_t type; 113 | uint16_t rsvd; 114 | uint32_t data_length; /* entire data length without hdr */ 115 | uint64_t offset; 116 | uint32_t id; 117 | 118 | uint32_t type_specific[3]; 119 | 120 | uint8_t data[0]; 121 | }; 122 | 123 | struct acrd_req { 124 | uint8_t proto_ver; 125 | uint8_t type; 126 | uint16_t rsvd; 127 | uint32_t data_length; 128 | uint64_t offset; 129 | uint32_t id; 130 | 131 | /* request specific header */ 132 | uint8_t opcode; 133 | uint8_t rsvd2; 134 | uint16_t flags; 135 | uint32_t size; 136 | uint32_t rsvd3; 137 | 138 | uint8_t data[0]; 139 | }; 140 | 141 | struct acrd_rsp { 142 | uint8_t proto_ver; 143 | uint8_t type; 144 | uint16_t rsvd; 145 | uint32_t data_length; 146 | uint64_t offset; 147 | uint32_t id; 148 | 149 | /* response specific header */ 150 | int32_t result; 151 | int64_t rsvd2; 152 | 153 | uint8_t data[0]; 154 | }; 155 | 156 | struct acrd_ntfy { 157 | uint8_t proto_ver; 158 | uint8_t type; 159 | uint16_t rsvd; 160 | uint32_t data_length; 161 | uint64_t offset; 162 | uint32_t id; 163 | 164 | /* notification specific header */ 165 | uint16_t events; 166 | uint16_t rsvd2; 167 | int64_t rsvd3; 168 | 169 | uint8_t data[0]; 170 | }; 171 | 172 | 173 | struct acrd_arg { 174 | uint32_t size; 175 | char data[0]; 176 | }; 177 | 178 | #define for_each_arg(arg, hdr) \ 179 | for (arg = (struct acrd_arg *)hdr->data; \ 180 | (uint8_t *)arg < (hdr)->data + (hdr)->data_length; \ 181 | arg = (struct acrd_arg *)((uint8_t *)arg + \ 182 | sizeof(arg->size) + arg->size)) 183 | 184 | /* Get the idx'th argument */ 185 | static inline const struct acrd_arg *get_arg(const void *p, int idx) 186 | { 187 | const struct acrd_common_hdr *hdr = p; 188 | const struct acrd_arg *arg; 189 | int i = 0; 190 | 191 | for_each_arg(arg, hdr) { 192 | if (i == idx) 193 | return arg; 194 | i++; 195 | } 196 | 197 | return NULL; 198 | } 199 | 200 | /* Add a new argument */ 201 | static inline void *add_arg(void *p, const void *data, uint32_t data_len) 202 | { 203 | struct acrd_common_hdr *hdr = p; 204 | struct acrd_arg *arg; 205 | 206 | hdr = realloc(hdr, sizeof(*hdr) + hdr->data_length + 207 | sizeof(data_len) + data_len); 208 | if (unlikely(!hdr)) { 209 | fprintf(stderr, "oom\n"); 210 | return NULL; 211 | } 212 | 213 | arg = (struct acrd_arg *)(hdr->data + hdr->data_length); 214 | arg->size = data_len; 215 | memcpy(arg->data, data, data_len); 216 | 217 | hdr->data_length += sizeof(data_len) + data_len; 218 | 219 | return hdr; 220 | } 221 | 222 | /* Append data to the last argument. This doesn't add a new argument */ 223 | static inline void *append_arg(void *p, const void *data, uint32_t data_len) 224 | { 225 | struct acrd_common_hdr *hdr = p; 226 | struct acrd_arg *arg, *last_arg = NULL; 227 | 228 | if (hdr->data_length == 0) 229 | return add_arg(p, data, data_len); 230 | 231 | hdr = realloc(hdr, sizeof(*hdr) + hdr->data_length + data_len); 232 | if (unlikely(!hdr)) { 233 | fprintf(stderr, "oom\n"); 234 | return NULL; 235 | } 236 | 237 | for_each_arg(arg, hdr) 238 | last_arg = arg; 239 | 240 | memcpy(last_arg->data + last_arg->size, data, data_len); 241 | last_arg->size += data_len; 242 | 243 | hdr->data_length += data_len; 244 | 245 | return hdr; 246 | } 247 | 248 | #endif /* __ACCORD_PROTO_H__ */ 249 | -------------------------------------------------------------------------------- /include/coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef __COROUTINE__ 2 | #define __COROUTINE__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "list.h" 9 | 10 | struct coroutine; 11 | 12 | typedef void coroutine_entry_func_t(void *opaque); 13 | 14 | struct coroutine *coroutine_create(coroutine_entry_func_t *entry); 15 | void coroutine_enter(struct coroutine *coroutine, void *opaque); 16 | void coroutine_yield(void); 17 | struct coroutine *coroutine_self(void); 18 | int in_coroutine(void); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/event.h: -------------------------------------------------------------------------------- 1 | #ifndef __EVENT_H__ 2 | #define __EVENT_H__ 3 | 4 | #include "list.h" 5 | 6 | struct event_info; 7 | 8 | typedef void (*event_handler_t)(int fd, int events, void *data); 9 | 10 | int init_event(int nr); 11 | int register_event(int fd, event_handler_t h, void *data); 12 | void unregister_event(int fd); 13 | int modify_event(int fd, unsigned int events); 14 | void event_loop(int timeout); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIST_H__ 2 | #define __LIST_H__ 3 | 4 | /* taken from linux kernel */ 5 | #include 6 | 7 | #undef offsetof 8 | #ifdef __compiler_offsetof 9 | #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER) 10 | #else 11 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 12 | #endif 13 | 14 | #define container_of(ptr, type, member) ({ \ 15 | const typeof(((type *)0)->member) (*__mptr) = (ptr); \ 16 | (type *)((char *)__mptr - offsetof(type, member)); }) 17 | 18 | struct list_head { 19 | struct list_head *next, *prev; 20 | }; 21 | 22 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 23 | 24 | #define LIST_HEAD(name) \ 25 | struct list_head name = LIST_HEAD_INIT(name) 26 | 27 | static inline void INIT_LIST_HEAD(struct list_head *list) 28 | { 29 | list->next = list; 30 | list->prev = list; 31 | } 32 | 33 | #define list_first_entry(ptr, type, member) \ 34 | list_entry((ptr)->next, type, member) 35 | 36 | static inline int list_empty(const struct list_head *head) 37 | { 38 | return head->next == head; 39 | } 40 | 41 | #define list_entry(ptr, type, member) \ 42 | container_of(ptr, type, member) 43 | 44 | #define list_for_each(pos, head) \ 45 | for (pos = (head)->next; pos != (head); pos = pos->next) 46 | 47 | #define list_for_each_entry(pos, head, member) \ 48 | for (pos = list_entry((head)->next, typeof(*pos), member); \ 49 | &pos->member != (head); \ 50 | pos = list_entry(pos->member.next, typeof(*pos), member)) 51 | 52 | #define list_for_each_entry_safe(pos, n, head, member) \ 53 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 54 | n = list_entry(pos->member.next, typeof(*pos), member); \ 55 | &pos->member != (head); \ 56 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 57 | 58 | static inline void __list_add(struct list_head *new, 59 | struct list_head *prev, 60 | struct list_head *next) 61 | { 62 | next->prev = new; 63 | new->next = next; 64 | new->prev = prev; 65 | prev->next = new; 66 | } 67 | 68 | static inline void list_add(struct list_head *new, struct list_head *head) 69 | { 70 | __list_add(new, head, head->next); 71 | } 72 | 73 | static inline void list_add_tail(struct list_head *new, struct list_head *head) 74 | { 75 | __list_add(new, head->prev, head); 76 | } 77 | 78 | static inline void __list_del(struct list_head *prev, struct list_head *next) 79 | { 80 | next->prev = prev; 81 | prev->next = next; 82 | } 83 | 84 | static inline void list_del(struct list_head *entry) 85 | { 86 | __list_del(entry->prev, entry->next); 87 | entry->next = entry->prev = NULL; 88 | } 89 | 90 | static inline void list_del_init(struct list_head *entry) 91 | { 92 | __list_del(entry->prev, entry->next); 93 | INIT_LIST_HEAD(entry); 94 | } 95 | 96 | static inline void __list_splice(const struct list_head *list, 97 | struct list_head *prev, 98 | struct list_head *next) 99 | { 100 | struct list_head *first = list->next; 101 | struct list_head *last = list->prev; 102 | 103 | first->prev = prev; 104 | prev->next = first; 105 | 106 | last->next = next; 107 | next->prev = last; 108 | } 109 | 110 | static inline void list_splice_init(struct list_head *list, 111 | struct list_head *head) 112 | { 113 | if (!list_empty(list)) { 114 | __list_splice(list, head, head->next); 115 | INIT_LIST_HEAD(list); 116 | } 117 | } 118 | 119 | static inline void list_splice_tail_init(struct list_head *list, 120 | struct list_head *head) 121 | { 122 | if (!list_empty(list)) { 123 | __list_splice(list, head->prev, head); 124 | INIT_LIST_HEAD(list); 125 | } 126 | } 127 | #endif 128 | -------------------------------------------------------------------------------- /include/net.h: -------------------------------------------------------------------------------- 1 | #ifndef __NET_H__ 2 | #define __NET_H__ 3 | 4 | #include 5 | #include "accord_proto.h" 6 | 7 | struct co_buffer { 8 | int offset; 9 | int len; 10 | char *buf; 11 | }; 12 | 13 | int do_read(int sockfd, void *buf, int len); 14 | int do_writev(int sockfd, struct iovec *iov, int len, int offset); 15 | void do_co_read(struct co_buffer *cob, void *buf, size_t count); 16 | int connect_to(const char *name, int port, int idle, int intvl, int cnt); 17 | int create_listen_ports(int port, int (*callback)(int fd, void *), void *data); 18 | 19 | int set_nonblocking(int fd); 20 | int set_nodelay(int fd); 21 | int set_cork(int fd); 22 | int unset_cork(int fd); 23 | int set_keepalive(int fd,int idle, int intvl, int cnt); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTIL_H__ 2 | #define __UTIL_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 10 | #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) 11 | 12 | #if __BYTE_ORDER == __LITTLE_ENDIAN 13 | #define __cpu_to_be16(x) bswap_16(x) 14 | #define __cpu_to_be32(x) bswap_32(x) 15 | #define __cpu_to_be64(x) bswap_64(x) 16 | #define __be16_to_cpu(x) bswap_16(x) 17 | #define __be32_to_cpu(x) bswap_32(x) 18 | #define __be64_to_cpu(x) bswap_64(x) 19 | #define __cpu_to_le32(x) (x) 20 | #else 21 | #define __cpu_to_be16(x) (x) 22 | #define __cpu_to_be32(x) (x) 23 | #define __cpu_to_be64(x) (x) 24 | #define __be16_to_cpu(x) (x) 25 | #define __be32_to_cpu(x) (x) 26 | #define __be64_to_cpu(x) (x) 27 | #define __cpu_to_le32(x) bswap_32(x) 28 | #endif 29 | 30 | static inline int before(uint32_t seq1, uint32_t seq2) 31 | { 32 | return (int32_t)(seq1 - seq2) < 0; 33 | } 34 | 35 | static inline int after(uint32_t seq1, uint32_t seq2) 36 | { 37 | return (int32_t)(seq2 - seq1) < 0; 38 | } 39 | 40 | #define min(x, y) ({ \ 41 | typeof(x) _x = (x); \ 42 | typeof(y) _y = (y); \ 43 | (void) (&_x == &_y); \ 44 | _x < _y ? _x : _y; }) 45 | 46 | #define max(x, y) ({ \ 47 | typeof(x) _x = (x); \ 48 | typeof(y) _y = (y); \ 49 | (void) (&_x == &_y); \ 50 | _x > _y ? _x : _y; }) 51 | 52 | static inline void *zalloc(size_t size) 53 | { 54 | return calloc(1, size); 55 | } 56 | 57 | #define likely(x) __builtin_expect(!!(x), 1) 58 | #define unlikely(x) __builtin_expect(!!(x), 0) 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /include/work.h: -------------------------------------------------------------------------------- 1 | #ifndef __WORK_H__ 2 | #define __WORK_H__ 3 | 4 | #include "list.h" 5 | 6 | struct work; 7 | struct work_queue; 8 | 9 | typedef void (*work_func_t)(struct list_head *); 10 | 11 | struct work_queue *init_work_queue(work_func_t fn, int interval); 12 | void queue_work(struct work_queue *wq, struct list_head *w_list); 13 | void exit_work_queue(struct work_queue *wq); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -g -O3 -Wall -Wstrict-prototypes -I../include 2 | CFLAGS += -D_GNU_SOURCE -DNDEBUG 3 | 4 | .PHONY:clean 5 | clean: 6 | rm -f *.[od] 7 | 8 | # support for GNU Flymake 9 | check-syntax: 10 | $(CC) $(CFLAGS) -fsyntax-only $(CHK_SOURCES) 11 | -------------------------------------------------------------------------------- /lib/coroutine.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 MORITA Kazutaka 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | * 17 | * This code is based on coroutine-ucontext.c and qemu-coroutine.c from QEMU: 18 | * Copyright (C) 2006 Anthony Liguori 19 | * Copyright (C) 2011 Stefan Hajnoczi 20 | * Copyright (C) 2011 Kevin Wolf 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "util.h" 36 | #include "coroutine.h" 37 | 38 | enum co_action { 39 | COROUTINE_YIELD = 1, 40 | COROUTINE_TERMINATE = 2, 41 | }; 42 | 43 | /* Maximum free pool size prevents holding too many freed coroutines */ 44 | #ifdef COROUTINE_DEBUG 45 | #define POOL_MAX_SIZE 1 46 | #else 47 | #define POOL_MAX_SIZE 1024 48 | #endif 49 | 50 | #define STACK_MAX_SIZE (1 << 16) /* 64 KB */ 51 | 52 | struct coroutine { 53 | coroutine_entry_func_t *entry; 54 | void *entry_arg; 55 | struct coroutine *caller; 56 | struct list_head pool_next; 57 | struct list_head co_queue_next; 58 | }; 59 | 60 | struct co_ucontext { 61 | struct coroutine base; 62 | void *stack; 63 | jmp_buf env; 64 | }; 65 | 66 | /** 67 | * Per-thread coroutine bookkeeping 68 | */ 69 | struct co_thread_state{ 70 | /** Currently executing coroutine */ 71 | struct coroutine *current; 72 | 73 | /** Free list to speed up creation */ 74 | struct list_head pool; 75 | unsigned int pool_size; 76 | 77 | /** The default coroutine */ 78 | struct co_ucontext leader; 79 | }; 80 | 81 | static pthread_key_t thread_state_key; 82 | 83 | static enum co_action coroutine_switch(struct coroutine *from, 84 | struct coroutine *to, 85 | enum co_action action); 86 | 87 | /* 88 | * va_args to makecontext() must be type 'int', so passing 89 | * the pointer we need may require several int args. This 90 | * union is a quick hack to let us do that 91 | */ 92 | union cc_arg { 93 | void *p; 94 | int i[2]; 95 | }; 96 | 97 | static struct co_thread_state *coroutine_get_thread_state(void) 98 | { 99 | struct co_thread_state *s = pthread_getspecific(thread_state_key); 100 | 101 | if (!s) { 102 | s = zalloc(sizeof(*s)); 103 | if (!s) 104 | abort(); 105 | s->current = &s->leader.base; 106 | INIT_LIST_HEAD(&s->pool); 107 | pthread_setspecific(thread_state_key, s); 108 | } 109 | return s; 110 | } 111 | 112 | static void coroutine_thread_cleanup(void *opaque) 113 | { 114 | struct co_thread_state *s = opaque; 115 | struct coroutine *co; 116 | struct coroutine *tmp; 117 | 118 | list_for_each_entry_safe(co, tmp, &s->pool, pool_next) { 119 | free(container_of(co, struct co_ucontext, base)->stack); 120 | free(co); 121 | } 122 | free(s); 123 | } 124 | 125 | static void __attribute__((constructor)) coroutine_init(void) 126 | { 127 | int ret; 128 | 129 | ret = pthread_key_create(&thread_state_key, coroutine_thread_cleanup); 130 | if (ret != 0) { 131 | fprintf(stderr, "unable to create leader key: %m\n"); 132 | abort(); 133 | } 134 | } 135 | 136 | static void coroutine_trampoline(int i0, int i1) 137 | { 138 | union cc_arg arg; 139 | struct co_ucontext *self; 140 | struct coroutine *co; 141 | 142 | arg.i[0] = i0; 143 | arg.i[1] = i1; 144 | self = arg.p; 145 | co = &self->base; 146 | 147 | /* Initialize longjmp environment and switch back the caller */ 148 | if (!setjmp(self->env)) 149 | longjmp(*(jmp_buf *)co->entry_arg, 1); 150 | 151 | for (;;) { 152 | co->entry(co->entry_arg); 153 | coroutine_switch(co, co->caller, COROUTINE_TERMINATE); 154 | } 155 | } 156 | 157 | #ifdef COROUTINE_DEBUG 158 | 159 | #define MAGIC_NUMBER 0x1234567890123456 160 | 161 | static void init_stack(struct co_ucontext *co) 162 | { 163 | uint64_t *stack = co->stack; 164 | int i; 165 | 166 | for (i = 0; i < STACK_MAX_SIZE / sizeof(stack[0]); i++) 167 | stack[i] = MAGIC_NUMBER; 168 | } 169 | 170 | static int get_stack_size(struct co_ucontext *co) 171 | { 172 | uint64_t *stack = co->stack; 173 | int i; 174 | 175 | for (i = 0; i < STACK_MAX_SIZE / sizeof(stack[0]); i++) 176 | if (stack[i] != MAGIC_NUMBER) 177 | break; 178 | 179 | if (i == 0) { 180 | fprintf(stderr, "stack overflow\n"); 181 | fflush(stderr); 182 | abort(); 183 | } 184 | 185 | return STACK_MAX_SIZE - i * sizeof(stack[0]); 186 | } 187 | 188 | #endif 189 | 190 | static struct coroutine *__coroutine_new(void) 191 | { 192 | const size_t stack_size = STACK_MAX_SIZE; 193 | struct co_ucontext *co; 194 | ucontext_t old_uc, uc; 195 | jmp_buf old_env; 196 | union cc_arg arg = {0}; 197 | 198 | /* The ucontext functions preserve signal masks which incurs a 199 | * system call overhead. setjmp()/longjmp() does not preserve 200 | * signal masks but only works on the current stack. Since we 201 | * need a way to create and switch to a new stack, use the 202 | * ucontext functions for that but setjmp()/longjmp() for 203 | * everything else. 204 | */ 205 | 206 | if (getcontext(&uc) == -1) 207 | abort(); 208 | 209 | co = zalloc(sizeof(*co)); 210 | if (!co) 211 | abort(); 212 | co->stack = zalloc(stack_size); 213 | if (!co->stack) 214 | abort(); 215 | #ifdef COROUTINE_DEBUG 216 | init_stack(co); 217 | #endif 218 | co->base.entry_arg = &old_env; /* stash away our jmp_buf */ 219 | 220 | uc.uc_link = &old_uc; 221 | uc.uc_stack.ss_sp = co->stack; 222 | uc.uc_stack.ss_size = stack_size; 223 | uc.uc_stack.ss_flags = 0; 224 | 225 | arg.p = co; 226 | 227 | makecontext(&uc, (void (*)(void))coroutine_trampoline, 228 | 2, arg.i[0], arg.i[1]); 229 | 230 | /* swapcontext() in, longjmp() back out */ 231 | if (!setjmp(old_env)) 232 | swapcontext(&old_uc, &uc); 233 | 234 | return &co->base; 235 | } 236 | 237 | static struct coroutine *coroutine_new(void) 238 | { 239 | struct co_thread_state *s = coroutine_get_thread_state(); 240 | struct coroutine *co; 241 | 242 | if (!list_empty(&s->pool)) { 243 | co = list_first_entry(&s->pool, struct coroutine, pool_next); 244 | list_del(&co->pool_next); 245 | s->pool_size--; 246 | } else 247 | co = __coroutine_new(); 248 | 249 | return co; 250 | } 251 | 252 | static void coroutine_delete(struct coroutine *co_) 253 | { 254 | struct co_thread_state *s = coroutine_get_thread_state(); 255 | struct co_ucontext *co = container_of(co_, struct co_ucontext, base); 256 | 257 | #ifdef COROUTINE_DEBUG 258 | fprintf(stdout, "%d bytes are consumed\n", get_stack_size(co)); 259 | #endif 260 | 261 | if (s->pool_size < POOL_MAX_SIZE) { 262 | list_add(&co->base.pool_next, &s->pool); 263 | co->base.caller = NULL; 264 | s->pool_size++; 265 | return; 266 | } 267 | 268 | free(co->stack); 269 | free(co); 270 | } 271 | 272 | static enum co_action coroutine_switch(struct coroutine *from_, 273 | struct coroutine *to_, 274 | enum co_action action) 275 | { 276 | struct co_ucontext *from = container_of(from_, struct co_ucontext, base); 277 | struct co_ucontext *to = container_of(to_, struct co_ucontext, base); 278 | struct co_thread_state *s = coroutine_get_thread_state(); 279 | int ret; 280 | 281 | s->current = to_; 282 | 283 | ret = setjmp(from->env); 284 | if (ret == 0) 285 | longjmp(to->env, action); 286 | 287 | return ret; 288 | } 289 | 290 | struct coroutine *coroutine_self(void) 291 | { 292 | struct co_thread_state *s = coroutine_get_thread_state(); 293 | 294 | return s->current; 295 | } 296 | 297 | int in_coroutine(void) 298 | { 299 | struct co_thread_state *s = pthread_getspecific(thread_state_key); 300 | 301 | return s && s->current->caller; 302 | } 303 | 304 | 305 | struct coroutine *coroutine_create(coroutine_entry_func_t *entry) 306 | { 307 | struct coroutine *co = coroutine_new(); 308 | co->entry = entry; 309 | return co; 310 | } 311 | 312 | static void coroutine_swap(struct coroutine *from, struct coroutine *to) 313 | { 314 | enum co_action ret; 315 | 316 | ret = coroutine_switch(from, to, COROUTINE_YIELD); 317 | 318 | switch (ret) { 319 | case COROUTINE_YIELD: 320 | return; 321 | case COROUTINE_TERMINATE: 322 | coroutine_delete(to); 323 | return; 324 | default: 325 | abort(); 326 | } 327 | } 328 | 329 | void coroutine_enter(struct coroutine *co, void *opaque) 330 | { 331 | struct coroutine *self = coroutine_self(); 332 | 333 | if (unlikely(co->caller)) { 334 | fprintf(stderr, "Co-routine re-entered recursively\n"); 335 | abort(); 336 | } 337 | 338 | co->caller = self; 339 | co->entry_arg = opaque; 340 | coroutine_swap(self, co); 341 | } 342 | 343 | void coroutine_yield(void) 344 | { 345 | struct coroutine *self = coroutine_self(); 346 | struct coroutine *to = self->caller; 347 | 348 | if (unlikely(!to)) { 349 | fprintf(stderr, "Co-routine is yielding to no one\n"); 350 | abort(); 351 | } 352 | 353 | self->caller = NULL; 354 | coroutine_swap(self, to); 355 | } 356 | -------------------------------------------------------------------------------- /lib/event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 MORITA Kazutaka 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "list.h" 23 | #include "util.h" 24 | #include "event.h" 25 | #include "coroutine.h" 26 | 27 | static int efd; 28 | static LIST_HEAD(events_list); 29 | 30 | struct event_info { 31 | event_handler_t handler; 32 | int fd; 33 | void *data; 34 | struct list_head ei_list; 35 | }; 36 | 37 | int init_event(int nr) 38 | { 39 | efd = epoll_create(nr); 40 | if (efd < 0) { 41 | fprintf(stderr, "can't create epoll fd\n"); 42 | return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | static struct event_info *lookup_event(int fd) 48 | { 49 | struct event_info *ei; 50 | 51 | list_for_each_entry(ei, &events_list, ei_list) { 52 | if (ei->fd == fd) 53 | return ei; 54 | } 55 | return NULL; 56 | } 57 | 58 | int register_event(int fd, event_handler_t h, void *data) 59 | { 60 | int ret; 61 | struct epoll_event ev; 62 | struct event_info *ei; 63 | 64 | ei = zalloc(sizeof(*ei)); 65 | if (!ei) 66 | return -ENOMEM; 67 | 68 | ei->fd = fd; 69 | ei->handler = h; 70 | ei->data = data; 71 | 72 | ev.events = EPOLLIN; 73 | ev.data.ptr = ei; 74 | 75 | ret = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); 76 | if (ret) { 77 | fprintf(stderr, "can't add epoll event, %m\n"); 78 | free(ei); 79 | } else 80 | list_add(&ei->ei_list, &events_list); 81 | 82 | return ret; 83 | } 84 | 85 | void unregister_event(int fd) 86 | { 87 | int ret; 88 | struct event_info *ei; 89 | 90 | ei = lookup_event(fd); 91 | if (!ei) { 92 | fprintf(stderr, "can't find a event\n"); 93 | return; 94 | } 95 | 96 | ret = epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL); 97 | if (ret) 98 | fprintf(stderr, "can't del epoll event, %m\n"); 99 | 100 | list_del(&ei->ei_list); 101 | free(ei); 102 | } 103 | 104 | int modify_event(int fd, unsigned int events) 105 | { 106 | int ret; 107 | struct epoll_event ev; 108 | struct event_info *ei; 109 | 110 | ei = lookup_event(fd); 111 | if (!ei) { 112 | fprintf(stderr, "can't find event info %d\n", fd); 113 | return 1; 114 | } 115 | 116 | memset(&ev, 0, sizeof(ev)); 117 | ev.events = events; 118 | ev.data.ptr = ei; 119 | 120 | ret = epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev); 121 | if (ret) { 122 | fprintf(stderr, "can't del epoll event, %m\n"); 123 | return 1; 124 | } 125 | return 0; 126 | } 127 | 128 | void event_loop(int timeout) 129 | { 130 | int i, nr; 131 | struct epoll_event events[128]; 132 | 133 | retry: 134 | nr = epoll_wait(efd, events, ARRAY_SIZE(events), timeout); 135 | if (nr < 0) { 136 | if (errno == EINTR) 137 | goto retry; 138 | fprintf(stderr, "epoll_wait failed, %m\n"); 139 | exit(1); 140 | } else if (nr) { 141 | for (i = 0; i < nr; i++) { 142 | struct event_info *ei; 143 | 144 | ei = (struct event_info *)events[i].data.ptr; 145 | ei->handler(ei->fd, events[i].events, ei->data); 146 | } 147 | } 148 | goto retry; 149 | } 150 | -------------------------------------------------------------------------------- /lib/net.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 MORITA Kazutaka 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "accord_proto.h" 31 | #include "util.h" 32 | #include "event.h" 33 | #include "net.h" 34 | 35 | int create_listen_ports(int port, int (*callback)(int fd, void *), void *data) 36 | { 37 | char servname[64]; 38 | int fd, ret, opt; 39 | int success = 0; 40 | struct addrinfo hints, *res, *res0; 41 | 42 | memset(servname, 0, sizeof(servname)); 43 | snprintf(servname, sizeof(servname), "%d", port); 44 | 45 | memset(&hints, 0, sizeof(hints)); 46 | hints.ai_socktype = SOCK_STREAM; 47 | hints.ai_flags = AI_PASSIVE; 48 | 49 | ret = getaddrinfo(NULL, servname, &hints, &res0); 50 | if (ret) { 51 | fprintf(stderr, "unable to get address info, %m\n"); 52 | return 1; 53 | } 54 | 55 | for (res = res0; res; res = res->ai_next) { 56 | fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 57 | if (fd < 0) 58 | continue; 59 | 60 | opt = 1; 61 | ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, 62 | sizeof(opt)); 63 | if (ret) 64 | fprintf(stderr, "can't set SO_REUSEADDR, %m\n"); 65 | 66 | opt = 1; 67 | if (res->ai_family == AF_INET6) { 68 | ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, 69 | sizeof(opt)); 70 | if (ret) { 71 | close(fd); 72 | continue; 73 | } 74 | } 75 | 76 | ret = bind(fd, res->ai_addr, res->ai_addrlen); 77 | if (ret) { 78 | fprintf(stderr, "can't bind server socket, %m\n"); 79 | close(fd); 80 | continue; 81 | } 82 | 83 | ret = listen(fd, SOMAXCONN); 84 | if (ret) { 85 | fprintf(stderr, "can't listen to server socket, %m\n"); 86 | close(fd); 87 | continue; 88 | } 89 | 90 | ret = set_nonblocking(fd); 91 | if (ret < 0) { 92 | close(fd); 93 | continue; 94 | } 95 | 96 | ret = callback(fd, data); 97 | if (ret) { 98 | close(fd); 99 | continue; 100 | } 101 | 102 | success++; 103 | } 104 | 105 | freeaddrinfo(res0); 106 | 107 | if (!success) 108 | fprintf(stderr, "can't create a listen fd\n"); 109 | 110 | return !success; 111 | } 112 | 113 | int connect_to(const char *name, int port, int idle, int intvl, int cnt) 114 | { 115 | char buf[64]; 116 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 117 | int fd, ret; 118 | struct addrinfo hints, *res, *res0; 119 | struct linger linger_opt = {1, 0}; 120 | 121 | memset(&hints, 0, sizeof(hints)); 122 | snprintf(buf, sizeof(buf), "%d", port); 123 | 124 | hints.ai_socktype = SOCK_STREAM; 125 | 126 | ret = getaddrinfo(name, buf, &hints, &res0); 127 | if (ret) { 128 | fprintf(stderr, "unable to get address info, %m\n"); 129 | return -1; 130 | } 131 | 132 | for (res = res0; res; res = res->ai_next) { 133 | ret = getnameinfo(res->ai_addr, res->ai_addrlen, 134 | hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), 135 | NI_NUMERICHOST | NI_NUMERICSERV); 136 | if (ret) 137 | continue; 138 | 139 | fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 140 | if (fd < 0) 141 | continue; 142 | 143 | ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_opt, 144 | sizeof(linger_opt)); 145 | if (ret) { 146 | fprintf(stderr, "can't set SO_LINGER, %m\n"); 147 | close(fd); 148 | continue; 149 | } 150 | 151 | ret = connect(fd, res->ai_addr, res->ai_addrlen); 152 | if (ret) 153 | fprintf(stderr, "failed to connect to %s:%d, %s\n", 154 | name, port, strerror(errno)); 155 | else { 156 | ret = set_keepalive(fd, idle, intvl, cnt); 157 | if (ret) 158 | fprintf(stderr, "failed to set keepalives\n"); 159 | else 160 | goto success; 161 | } 162 | 163 | close(fd); 164 | } 165 | fd = -1; 166 | success: 167 | freeaddrinfo(res0); 168 | return fd; 169 | } 170 | 171 | int do_read(int sockfd, void *buf, int len) 172 | { 173 | int rc, ret = 0; 174 | reread: 175 | rc = read(sockfd, buf, len); 176 | if (rc == 0) { 177 | if (ret) 178 | return ret; 179 | else 180 | return -1; 181 | } else if (rc < 0) { 182 | if (errno == EINTR) 183 | goto reread; 184 | if (errno == EAGAIN) 185 | return ret; 186 | 187 | return -1; 188 | } 189 | 190 | len -= rc; 191 | ret += rc; 192 | buf = (char *)buf + rc; 193 | if (len) 194 | goto reread; 195 | 196 | return ret; 197 | } 198 | 199 | int do_writev(int sockfd, struct iovec *iov, int len, int offset) 200 | { 201 | int ret, diff, iovcnt; 202 | struct iovec *last_iov; 203 | 204 | /* last_iov is inclusive, so count from one. */ 205 | iovcnt = 1; 206 | last_iov = iov; 207 | len += offset; 208 | 209 | while (last_iov->iov_len < len) { 210 | len -= last_iov->iov_len; 211 | 212 | last_iov++; 213 | iovcnt++; 214 | } 215 | 216 | diff = last_iov->iov_len - len; 217 | last_iov->iov_len -= diff; 218 | 219 | while (iov->iov_len <= offset) { 220 | offset -= iov->iov_len; 221 | 222 | iov++; 223 | iovcnt--; 224 | } 225 | 226 | iov->iov_base = (char *) iov->iov_base + offset; 227 | iov->iov_len -= offset; 228 | again: 229 | ret = writev(sockfd, iov, iovcnt); 230 | if (ret == -1 && errno == EINTR) 231 | goto again; 232 | 233 | /* Undo the changes above */ 234 | iov->iov_base = (char *) iov->iov_base - offset; 235 | iov->iov_len += offset; 236 | last_iov->iov_len += diff; 237 | return ret; 238 | } 239 | 240 | void do_co_read(struct co_buffer *cob, void *buf, size_t count) 241 | { 242 | int done = 0, len; 243 | 244 | while (cob->offset + count > cob->len) { 245 | len = cob->len - cob->offset; 246 | memcpy(buf + done, cob->buf + cob->offset, len); 247 | 248 | done += len; 249 | count -= len; 250 | 251 | coroutine_yield(); 252 | } 253 | 254 | memcpy(buf + done, cob->buf + cob->offset, count); 255 | cob->offset += count; 256 | } 257 | 258 | 259 | int set_nonblocking(int fd) 260 | { 261 | int ret; 262 | 263 | ret = fcntl(fd, F_GETFL); 264 | if (ret < 0) { 265 | fprintf(stderr, "can't fcntl (F_GETFL), %m\n"); 266 | close(fd); 267 | } else { 268 | ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); 269 | if (ret < 0) 270 | fprintf(stderr, "can't fcntl (O_NONBLOCK), %m\n"); 271 | } 272 | 273 | return ret; 274 | } 275 | 276 | int set_nodelay(int fd) 277 | { 278 | int ret, opt; 279 | 280 | opt = 1; 281 | ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); 282 | return ret; 283 | } 284 | 285 | int set_cork(int fd) 286 | { 287 | int opt = 1; 288 | 289 | return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); 290 | } 291 | 292 | int unset_cork(int fd) 293 | { 294 | int opt = 0; 295 | 296 | return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); 297 | } 298 | 299 | int set_keepalive(int fd, int idle, int intvl, int cnt) 300 | { 301 | int ret, opt = 1; 302 | 303 | ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&opt, sizeof(opt)); 304 | if (ret < 0) 305 | return ret; 306 | 307 | opt = idle; 308 | ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&opt, sizeof(opt)); 309 | if (ret < 0) 310 | return ret; 311 | 312 | opt = intvl; 313 | setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&opt, sizeof(opt)); 314 | if (ret < 0) 315 | return ret; 316 | 317 | opt = cnt; 318 | setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (void*)&opt, sizeof(opt)); 319 | if (ret < 0) 320 | return ret; 321 | 322 | return ret; 323 | } 324 | -------------------------------------------------------------------------------- /lib/work.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 MORITA Kazutaka 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | #include 18 | #include 19 | 20 | #include "list.h" 21 | #include "util.h" 22 | #include "work.h" 23 | 24 | static LIST_HEAD(work_queue_list); 25 | 26 | struct work_queue { 27 | struct list_head worker_queue_siblings; 28 | 29 | pthread_cond_t pending_cond; 30 | pthread_mutex_t pending_lock; 31 | struct list_head q; 32 | work_func_t fn; 33 | int interval; 34 | 35 | int stop; 36 | 37 | pthread_t worker_thread; 38 | }; 39 | 40 | void queue_work(struct work_queue *wq, struct list_head *w_list) 41 | { 42 | pthread_mutex_lock(&wq->pending_lock); 43 | 44 | list_add_tail(w_list, &wq->q); 45 | 46 | pthread_mutex_unlock(&wq->pending_lock); 47 | 48 | pthread_cond_signal(&wq->pending_cond); 49 | } 50 | 51 | static void *worker_routine(void *arg) 52 | { 53 | struct work_queue *wq = arg; 54 | struct list_head list; 55 | 56 | while (!wq->stop) { 57 | if (wq->interval) 58 | usleep(wq->interval * 1000); 59 | 60 | pthread_mutex_lock(&wq->pending_lock); 61 | retest: 62 | if (wq->stop) { 63 | pthread_mutex_unlock(&wq->pending_lock); 64 | pthread_exit(NULL); 65 | } 66 | 67 | if (list_empty(&wq->q)) { 68 | pthread_cond_wait(&wq->pending_cond, &wq->pending_lock); 69 | goto retest; 70 | } 71 | 72 | INIT_LIST_HEAD(&list); 73 | list_splice_init(&wq->q, &list); 74 | 75 | pthread_mutex_unlock(&wq->pending_lock); 76 | 77 | if (!list_empty(&list)) 78 | wq->fn(&list); 79 | } 80 | 81 | pthread_exit(NULL); 82 | } 83 | 84 | struct work_queue *init_work_queue(work_func_t fn, int interval) 85 | { 86 | int ret; 87 | struct work_queue *wq; 88 | 89 | wq = zalloc(sizeof(*wq)); 90 | if (!wq) 91 | return NULL; 92 | 93 | wq->fn = fn; 94 | wq->interval = interval; 95 | INIT_LIST_HEAD(&wq->q); 96 | 97 | pthread_cond_init(&wq->pending_cond, NULL); 98 | 99 | pthread_mutex_init(&wq->pending_lock, NULL); 100 | 101 | ret = pthread_create(&wq->worker_thread, NULL, worker_routine, wq); 102 | if (ret) { 103 | fprintf(stderr, "failed to create a worker thread, %s\n", 104 | strerror(ret)); 105 | goto destroy_threads; 106 | } 107 | 108 | list_add(&wq->worker_queue_siblings, &work_queue_list); 109 | 110 | return wq; 111 | destroy_threads: 112 | wq->stop = 1; 113 | 114 | pthread_join(wq->worker_thread, NULL); 115 | 116 | /* destroy_cond_mutex: */ 117 | pthread_cond_destroy(&wq->pending_cond); 118 | pthread_mutex_destroy(&wq->pending_lock); 119 | 120 | return NULL; 121 | } 122 | 123 | void exit_work_queue(struct work_queue *wq) 124 | { 125 | pthread_mutex_lock(&wq->pending_lock); 126 | wq->stop = 1; 127 | pthread_mutex_unlock(&wq->pending_lock); 128 | pthread_cond_broadcast(&wq->pending_cond); 129 | 130 | pthread_join(wq->worker_thread, NULL); 131 | 132 | pthread_cond_destroy(&wq->pending_cond); 133 | pthread_mutex_destroy(&wq->pending_lock); 134 | 135 | wq->stop = 0; 136 | } 137 | -------------------------------------------------------------------------------- /libacrd/Makefile: -------------------------------------------------------------------------------- 1 | libdir ?= $(PREFIX)/lib 2 | 3 | CFLAGS += -g -O3 -Wall -Wstrict-prototypes -I../include 4 | CFLAGS += -D_GNU_SOURCE -DNDEBUG -fPIC 5 | 6 | LIBRARIES = libacrd.a libacrd.so 7 | LIBACRD_OBJS = libacrd.o ../lib/net.o ../lib/work.o ../lib/coroutine.o 8 | LIBACRD_DEP = $(LIBACRD_OBJS:.o=.d) 9 | LIBACRD_VERSION = libacrd.map 10 | 11 | PCFILES = libacrd.pc 12 | 13 | ARFLAGS = cru 14 | RANLIB = ranlib 15 | 16 | .PHONY:all 17 | all: $(LIBRARIES) 18 | 19 | libacrd.a: $(LIBACRD_OBJS) 20 | $(AR) $(ARFLAGS) $@ $^ 21 | $(RANLIB) $@ 22 | 23 | libacrd.so: $(LIBACRD_OBJS) 24 | $(CC) -shared $^ -o $@ -Wl,-version-script=$(LIBACRD_VERSION) 25 | 26 | libacrd.pc: libacrd.pc.in Makefile 27 | rm -f $@-t $@ 28 | sed \ 29 | -e 's#@''PREFIX@#$(PREFIX)#g' \ 30 | -e 's#@''LIBDIR@#$(libdir)#g' \ 31 | -e 's#@''VERSION@#$(VERSION)#g' \ 32 | $< > $@-t 33 | chmod a-w $@-t 34 | mv $@-t $@ 35 | 36 | -include $(LIBACRD_DEP) 37 | 38 | %.o: %.c 39 | $(CC) -c $(CFLAGS) $*.c -o $*.o 40 | @$(CC) -MM $(CFLAGS) -MF $*.d -MT $*.o $*.c 41 | 42 | .PHONY:clean 43 | clean: 44 | echo $(ARFLAGS) 45 | echo $(RANLIB) 46 | rm -f *.[od] $(LIBRARIES) $(PCFILES) 47 | 48 | .PHONY:install 49 | install: $(LIBRARIES) $(PCFILES) 50 | install -d -m 755 $(DESTDIR)$(libdir) 51 | install -m 644 $(LIBRARIES) $(DESTDIR)$(libdir) 52 | install -d -m 755 $(DESTDIR)$(libdir)/pkgconfig 53 | install -m 644 $(PCFILES) $(DESTDIR)$(libdir)/pkgconfig 54 | 55 | # support for GNU Flymake 56 | check-syntax: 57 | $(CC) $(CFLAGS) -fsyntax-only $(CHK_SOURCES) 58 | -------------------------------------------------------------------------------- /libacrd/libacrd.map: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | acrd_init; 4 | acrd_close; 5 | acrd_write; 6 | acrd_read; 7 | acrd_del; 8 | acrd_list; 9 | acrd_copy; 10 | acrd_aio_setup; 11 | acrd_aio_release; 12 | acrd_aio_wait; 13 | acrd_aio_flush; 14 | acrd_aio_write; 15 | acrd_aio_read; 16 | acrd_aio_del; 17 | acrd_aio_list; 18 | acrd_aio_copy; 19 | acrd_tx_init; 20 | acrd_tx_close; 21 | acrd_tx_write; 22 | acrd_tx_read; 23 | acrd_tx_del; 24 | acrd_tx_cmp; 25 | acrd_tx_scmp; 26 | acrd_tx_copy; 27 | acrd_tx_atomic_inc; 28 | acrd_tx_commit; 29 | acrd_tx_aio_commit; 30 | acrd_add_watch; 31 | acrd_rm_watch; 32 | local: 33 | *; 34 | }; 35 | -------------------------------------------------------------------------------- /libacrd/libacrd.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=@LIBDIR@ 4 | includedir=${prefix}/include 5 | 6 | Name: libacrd 7 | Version: @VERSION@ 8 | Description: libacrd 9 | Requires: 10 | Libs: -L${libdir} -lacrd 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -g -O3 -Wall -Wstrict-prototypes -I../include 2 | CFLAGS += -D_GNU_SOURCE 3 | CFLAGS += $(shell pkg-config --cflags glib-2.0) 4 | LIBS += -lpthread 5 | 6 | TESTS = test-io test-aio test-txn test-watch test-concurrent test-coroutine 7 | TEST_XML_FILE = test-report.xml 8 | TEST_HTML_FILE = test-report.html 9 | TEST_LIBS += $(shell pkg-config --libs glib-2.0) 10 | 11 | PROGRAMS = acrdbench watch_nodes watch_files 12 | BDRBENCH_OBJS = acrdbench.o 13 | BDRBENCH_DEP = $(BDRBENCH_OBJS:.o=.d) 14 | WATCH_NODES_OBJS = watch_nodes.o 15 | WATCH_NODES_DEP = $(TEST_OBJS:.o=.d) 16 | WATCH_FILES_OBJS = watch_files.o 17 | WATCH_FILES_DEP = $(TEST_OBJS:.o=.d) 18 | 19 | TEST_IO_OBJS = test-io.o 20 | TEST_IO_DEP = $(TEST_IO_OBJS:.o=.d) 21 | TEST_AIO_OBJS = test-aio.o 22 | TEST_AIO_DEP = $(TEST_TXN_OBJS:.o=.d) 23 | TEST_TXN_OBJS = test-txn.o 24 | TEST_TXN_DEP = $(TEST_TXN_OBJS:.o=.d) 25 | TEST_WATCH_OBJS = test-watch.o 26 | TEST_WATCH_DEP = $(TEST_WATCH_OBJS:.o=.d) 27 | TEST_CONCURRENT_OBJS = test-concurrent.o 28 | TEST_CONCURRENT_DEP = $(TEST_CONCURRENT_OBJS:.o=.d) 29 | TEST_COROUTINE_OBJS = test-coroutine.o ../lib/coroutine.o 30 | TEST_COROUTINE_DEP = $(TEST_COROUTINE_OBJS:.o=.d) 31 | 32 | .PHONY:all 33 | all: $(PROGRAMS) 34 | 35 | acrdbench: $(BDRBENCH_OBJS) ../libacrd/libacrd.a 36 | $(CC) $^ -o $@ $(LIBS) 37 | 38 | watch_nodes: $(WATCH_NODES_OBJS) ../libacrd/libacrd.a 39 | $(CC) $^ -o $@ $(LIBS) 40 | 41 | watch_files: $(WATCH_FILES_OBJS) ../libacrd/libacrd.a 42 | $(CC) $^ -o $@ $(LIBS) 43 | 44 | test-io: $(TEST_IO_OBJS) ../libacrd/libacrd.a 45 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 46 | 47 | test-aio: $(TEST_AIO_OBJS) ../libacrd/libacrd.a 48 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 49 | 50 | test-txn: $(TEST_TXN_OBJS) ../libacrd/libacrd.a 51 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 52 | 53 | test-watch: $(TEST_WATCH_OBJS) ../libacrd/libacrd.a 54 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 55 | 56 | test-concurrent: $(TEST_CONCURRENT_OBJS) ../libacrd/libacrd.a 57 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 58 | 59 | test-coroutine: $(TEST_COROUTINE_OBJS) ../libacrd/libacrd.a 60 | $(CC) $^ -o $@ $(LIBS) $(TEST_LIBS) 61 | 62 | -include $(TEST_DEP) 63 | -include $(BDRBENCH_DEP) 64 | -include $(WATCH_NODES_DEP) 65 | -include $(WATCH_FILES_DEP) 66 | -include $(TEST_IO_DEP) 67 | -include $(TEST_AIO_DEP) 68 | -include $(TEST_TXN_DEP) 69 | -include $(TEST_WATCH_DEP) 70 | -include $(TEST_CONCURRENT_DEP) 71 | -include $(TEST_COROUTINE_DEP) 72 | 73 | %.o: %.c 74 | $(CC) -c $(CFLAGS) $*.c -o $*.o 75 | @$(CC) -MM $(CFLAGS) -MF $*.d -MT $*.o $*.c 76 | 77 | .PHONY:clean 78 | clean: 79 | rm -f *.[od] $(PROGRAMS) $(TEST_XML_FILE) 80 | 81 | .PHONY:test 82 | test: $(TESTS) 83 | 84 | .PHONY:check 85 | check: 86 | gtester -k -o $(TEST_XML_FILE) $(TESTS); \ 87 | gtester-report $(TEST_XML_FILE) > $(TEST_HTML_FILE) 88 | 89 | # support for GNU Flymake 90 | check-syntax: 91 | $(CC) $(CFLAGS) -fsyntax-only $(CHK_SOURCES) 92 | -------------------------------------------------------------------------------- /test/acrdbench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include "util.h" 27 | 28 | #define dprintf(fmt, args...) \ 29 | do { \ 30 | fprintf(stdout, "%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ 31 | } while (0) 32 | 33 | #define eprintf(fmt, args...) \ 34 | do { \ 35 | fprintf(stderr, "%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ 36 | } while (0) 37 | 38 | static int total_n_handled; 39 | static int total_n_launched; 40 | static size_t total_n_bytes; 41 | struct timeval total_time = {0, 0}; 42 | 43 | char *remotehost; 44 | int n_threads; 45 | int n_requests; 46 | int msg_size; 47 | int sync_mode; 48 | 49 | char localhost[HOST_NAME_MAX]; 50 | 51 | struct request_info { 52 | struct timeval started; 53 | struct acrd_handle *bh; 54 | }; 55 | 56 | static char *size_to_str(uint64_t _size, char *str, int str_size) 57 | { 58 | const char *units[] = {"MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; 59 | int i = 0; 60 | double size; 61 | 62 | size = (double)_size; 63 | size /= 1024 * 1024; 64 | while (i < ARRAY_SIZE(units) && size >= 1024) { 65 | i++; 66 | size /= 1024; 67 | } 68 | 69 | if (size >= 10) 70 | snprintf(str, str_size, "%.1lf %s", size, units[i]); 71 | else 72 | snprintf(str, str_size, "%.2lf %s", size, units[i]); 73 | 74 | return str; 75 | } 76 | 77 | 78 | static void acrdbench_aio_cb(struct acrd_handle *h, struct acrd_aiocb *acb, void *arg) 79 | { 80 | int n_handled; 81 | 82 | if (acb->result != ACRD_SUCCESS) 83 | printf("%d\n", acb->result); 84 | 85 | __sync_add_and_fetch(&total_n_bytes, msg_size); 86 | n_handled = __sync_add_and_fetch(&total_n_handled, 1); 87 | if (n_handled && (n_handled % 10000) == 0) 88 | printf("%d requests done\n", n_handled); 89 | } 90 | 91 | static int launch_request(struct request_info *ri) 92 | { 93 | char prefix[256], url[256]; 94 | char *buf; 95 | int ret = -1, i, prefix_len; 96 | int tm; 97 | struct acrd_aiocb *acb; 98 | 99 | gettimeofday(&ri->started, NULL); 100 | buf = calloc(1, msg_size); 101 | memset(buf, 0xFF, msg_size); 102 | tm = (unsigned)time(NULL); 103 | 104 | prefix_len = sprintf(prefix, "/tmp/%s/%x/", localhost, tm); 105 | strcpy(url, prefix); 106 | 107 | for (i = 0; i < n_requests; i++) { 108 | sprintf(url + prefix_len, "%d", __sync_add_and_fetch(&total_n_launched, 1)); 109 | 110 | acb = acrd_aio_setup(ri->bh, acrdbench_aio_cb, NULL); 111 | again: 112 | ret = acrd_aio_write(ri->bh, url, buf, msg_size, 0, ACRD_FLAG_CREATE | sync_mode, acb); 113 | if (ret != ACRD_SUCCESS) { 114 | if (ret == ACRD_ERR_AGAIN) { 115 | usleep(100000); 116 | goto again; 117 | } 118 | eprintf("err, %d\n", ret); 119 | exit(1); 120 | } 121 | } 122 | 123 | if (ret < 0) { 124 | eprintf("error\n"); 125 | return -1; 126 | } 127 | 128 | acrd_aio_flush(ri->bh); 129 | return 0; 130 | } 131 | 132 | static void *th(void *p) 133 | { 134 | struct request_info *ri; 135 | 136 | ri = malloc(sizeof(*ri)); 137 | 138 | ri->bh = acrd_init(remotehost, 9090, NULL, NULL, NULL); 139 | if (!ri->bh) { 140 | eprintf("failed to initialize library\n"); 141 | pthread_exit(NULL); 142 | } 143 | 144 | if (launch_request(ri) < 0) 145 | return NULL; 146 | 147 | return NULL; 148 | } 149 | 150 | int main(int argc, char *argv[]) 151 | { 152 | int i; 153 | pthread_t *threads; 154 | struct timeval start, end, total; 155 | double throughput; 156 | char s1[64], s2[64]; 157 | 158 | if (argc < 6) { 159 | printf("usage: acrdbench [host] [n_threads] [n_reqs] " 160 | "[msg_size] [sync_mode]\n"); 161 | return 1; 162 | } 163 | 164 | total_n_handled = 0; 165 | total_n_launched = 0; 166 | total_n_bytes = 0; 167 | 168 | gethostname(localhost, sizeof(localhost)); 169 | 170 | setvbuf(stdout, NULL, _IONBF, 0); 171 | 172 | remotehost = argv[1]; 173 | n_threads = atoi(argv[2]); 174 | n_requests = atoi(argv[3]); 175 | msg_size = atoi(argv[4]); 176 | if (!strcasecmp(argv[5], "nosync")) 177 | sync_mode = 0; 178 | else if (!strcasecmp(argv[5], "sync")) 179 | sync_mode = ACRD_FLAG_SYNC; 180 | else { 181 | fprintf(stderr, "sync mode must be \"nosync\" or \"sync\"\n"); 182 | return 1; 183 | } 184 | 185 | gettimeofday(&start, NULL); 186 | 187 | threads = malloc(sizeof(*threads) * n_threads); 188 | for (i = 0; i < n_threads; i++) 189 | pthread_create(threads + i, NULL, th, NULL); 190 | 191 | for (i = 0; i < n_threads; i++) 192 | pthread_join(threads[i], NULL); 193 | 194 | gettimeofday(&end, NULL); 195 | timersub(&end, &start, &total); 196 | 197 | if (!total_n_handled) { 198 | puts("Nothing worked. You probably did something dumb."); 199 | return 0; 200 | } 201 | 202 | throughput = total_n_handled / 203 | (total.tv_sec + ((double)total.tv_usec)/1000000.0); 204 | 205 | printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" 206 | "%s write. (%s/sec)\n", 207 | total_n_handled, (int)total.tv_sec, (int)total.tv_usec, 208 | throughput, 209 | size_to_str(total_n_bytes, s1, sizeof(s1)), 210 | size_to_str(1000000 * total_n_bytes / (1000000 * total.tv_sec + total.tv_usec), s2, sizeof(s2))); 211 | 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /test/test-aio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | #include "util.h" 17 | #include "list.h" 18 | 19 | struct acrd_fixture { 20 | struct acrd_handle *handle; 21 | }; 22 | 23 | struct acrd_path_list_entry { 24 | char *path; 25 | 26 | struct list_head list; 27 | }; 28 | 29 | static void test_aio_list_cb(struct acrd_handle *h, const char *path, void *arg) 30 | { 31 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 32 | struct list_head *head = arg; 33 | 34 | entry->path = strdup(path); 35 | list_add_tail(&entry->list, head); 36 | } 37 | 38 | static void test_aio_setup(struct acrd_fixture *fixture, gconstpointer p) 39 | { 40 | struct acrd_handle *h; 41 | LIST_HEAD(path_list); 42 | struct acrd_listcb listcb = { 43 | .cb = test_aio_list_cb, 44 | .arg = &path_list, 45 | }; 46 | struct acrd_path_list_entry *entry, *n; 47 | 48 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 49 | g_assert(h != NULL); 50 | 51 | /* cleanup */ 52 | acrd_list(h, "/tmp/", 0, &listcb); 53 | list_for_each_entry_safe(entry, n, &path_list, list) { 54 | acrd_del(h, entry->path, 0); 55 | 56 | free(entry->path); 57 | list_del(&entry->list); 58 | free(entry); 59 | } 60 | 61 | fixture->handle = h; 62 | } 63 | 64 | static void test_aio_teardown(struct acrd_fixture *fixture, gconstpointer p) 65 | { 66 | acrd_close(fixture->handle); 67 | } 68 | 69 | #define TEST_ACB_MAX 1000 70 | 71 | static void test_aio_wait_cb(struct acrd_handle *h, struct acrd_aiocb *acb, void *arg) 72 | { 73 | struct acrd_aiocb **acbs = arg; 74 | int i, expect = 1; 75 | 76 | for (i = 0; i < TEST_ACB_MAX; i++) { 77 | if (acbs[i] == acb) 78 | expect = 0; 79 | 80 | g_assert(acbs[i]->done == expect); 81 | } 82 | } 83 | 84 | static void test_aio_wait(struct acrd_fixture *fixture, gconstpointer p) 85 | { 86 | struct acrd_handle *h = fixture->handle; 87 | const char data[] = "data"; 88 | int i, ret; 89 | struct acrd_aiocb *acbs[TEST_ACB_MAX]; 90 | 91 | 92 | for (i = 0; i < TEST_ACB_MAX; i++) 93 | acbs[i] = acrd_aio_setup(h, test_aio_wait_cb, acbs); 94 | 95 | for (i = 0; i < TEST_ACB_MAX; i++) { 96 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, 0, acbs[i]); 97 | g_assert(ret == ACRD_SUCCESS); 98 | } 99 | 100 | for (i = 0; i < TEST_ACB_MAX; i++) { 101 | acrd_aio_wait(h, acbs[i]); 102 | g_assert(acbs[i]->done == 1); 103 | } 104 | 105 | for (i = 0; i < TEST_ACB_MAX; i++) 106 | acrd_aio_release(h, acbs[i]); 107 | } 108 | 109 | static void test_aio_flush(struct acrd_fixture *fixture, gconstpointer p) 110 | { 111 | struct acrd_handle *h = fixture->handle; 112 | const char data[] = "data"; 113 | int i, ret; 114 | struct acrd_aiocb *acbs[TEST_ACB_MAX]; 115 | 116 | for (i = 0; i < TEST_ACB_MAX; i++) 117 | acbs[i] = acrd_aio_setup(h, test_aio_wait_cb, acbs); 118 | 119 | for (i = 0; i < TEST_ACB_MAX; i++) { 120 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, 0, acbs[i]); 121 | g_assert(ret == ACRD_SUCCESS); 122 | } 123 | 124 | acrd_aio_flush(h); 125 | for (i = 0; i < TEST_ACB_MAX; i++) { 126 | g_assert(acbs[i]->done == 1); 127 | acrd_aio_release(h, acbs[i]); 128 | } 129 | } 130 | 131 | static void test_aio_cb(struct acrd_handle *h, struct acrd_aiocb *acb, void *arg) 132 | { 133 | uint32_t *result = arg; 134 | 135 | *result = acb->result; 136 | } 137 | 138 | static void test_aio_create(struct acrd_fixture *fixture, gconstpointer p) 139 | { 140 | struct acrd_handle *h = fixture->handle; 141 | const char data[] = "data"; 142 | int ret; 143 | struct acrd_aiocb *acb1, *acb2; 144 | uint32_t res1, res2; 145 | 146 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 147 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb1); 148 | g_assert(ret == ACRD_SUCCESS); 149 | 150 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 151 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb2); 152 | g_assert(ret == ACRD_SUCCESS); 153 | 154 | acrd_aio_flush(h); 155 | g_assert(res1 == ACRD_SUCCESS); 156 | g_assert(res2 == ACRD_SUCCESS); 157 | acrd_aio_release(h, acb1); 158 | acrd_aio_release(h, acb2); 159 | } 160 | 161 | static void test_aio_write(struct acrd_fixture *fixture, gconstpointer p) 162 | { 163 | struct acrd_handle *h = fixture->handle; 164 | const char data1[] = "data"; 165 | const char data2[] = "newdata"; 166 | char retdata[32]; 167 | uint32_t retdata_len; 168 | int ret; 169 | struct acrd_aiocb *acb1, *acb2, *acb3, *acb4; 170 | uint32_t res1, res2, res3, res4; 171 | 172 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 173 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1), 0, 0, acb1); 174 | g_assert(ret == ACRD_SUCCESS); 175 | 176 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 177 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE, acb2); 178 | g_assert(ret == ACRD_SUCCESS); 179 | 180 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 181 | ret = acrd_aio_write(h, "/tmp/0", data2, sizeof(data2), 0, 0, acb3); 182 | g_assert(ret == ACRD_SUCCESS); 183 | 184 | acb4 = acrd_aio_setup(h, test_aio_cb, &res4); 185 | retdata_len = sizeof(retdata); 186 | ret = acrd_aio_read(h, "/tmp/0", retdata, &retdata_len, 0, 0, acb4); 187 | g_assert(ret == ACRD_SUCCESS); 188 | 189 | acrd_aio_flush(h); 190 | g_assert(res1 == ACRD_ERR_NOTFOUND); 191 | g_assert(res2 == ACRD_SUCCESS); 192 | g_assert(res3 == ACRD_SUCCESS); 193 | g_assert(res4 == ACRD_SUCCESS); 194 | acrd_aio_release(h, acb1); 195 | acrd_aio_release(h, acb2); 196 | acrd_aio_release(h, acb3); 197 | acrd_aio_release(h, acb4); 198 | 199 | g_assert(retdata_len == sizeof(data2)); 200 | g_assert_cmpstr(retdata, ==, data2); 201 | } 202 | 203 | static void test_aio_partial_write(struct acrd_fixture *fixture, gconstpointer p) 204 | { 205 | struct acrd_handle *h = fixture->handle; 206 | const char data[] = "data"; 207 | int ret; 208 | struct acrd_aiocb *acb1, *acb2, *acb3; 209 | uint32_t res1, res2, res3; 210 | char retdata[32]; 211 | uint32_t retdata_len; 212 | 213 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 214 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb1); 215 | g_assert(ret == ACRD_SUCCESS); 216 | 217 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 218 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 2, 0, acb2); 219 | g_assert(ret == ACRD_SUCCESS); 220 | 221 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 222 | retdata_len = sizeof(retdata); 223 | ret = acrd_aio_read(h, "/tmp/0", retdata, &retdata_len, 0, 0, acb3); 224 | g_assert(ret == ACRD_SUCCESS); 225 | 226 | acrd_aio_flush(h); 227 | g_assert(res1 == ACRD_SUCCESS); 228 | g_assert(res2 == ACRD_SUCCESS); 229 | g_assert(res3 == ACRD_SUCCESS); 230 | g_assert(retdata_len == 7); 231 | g_assert_cmpstr(retdata, ==, "dadata"); 232 | acrd_aio_release(h, acb1); 233 | acrd_aio_release(h, acb2); 234 | acrd_aio_release(h, acb3); 235 | } 236 | 237 | static void test_aio_sync(struct acrd_fixture *fixture, gconstpointer p) 238 | { 239 | struct acrd_handle *h = fixture->handle; 240 | const char data1[] = "data"; 241 | const char data2[] = "newdata"; 242 | char retdata[32]; 243 | uint32_t retdata_len; 244 | int ret; 245 | struct acrd_aiocb *acb1, *acb2, *acb3, *acb4; 246 | uint32_t res1, res2, res3, res4; 247 | 248 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 249 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1), 0, 250 | ACRD_FLAG_SYNC, acb1); 251 | g_assert(ret == ACRD_SUCCESS); 252 | 253 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 254 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1), 0, 255 | ACRD_FLAG_SYNC | ACRD_FLAG_CREATE, acb2); 256 | g_assert(ret == ACRD_SUCCESS); 257 | 258 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 259 | ret = acrd_aio_write(h, "/tmp/0", data2, sizeof(data2), 0, 260 | ACRD_FLAG_SYNC, acb3); 261 | g_assert(ret == ACRD_SUCCESS); 262 | 263 | acb4 = acrd_aio_setup(h, test_aio_cb, &res4); 264 | retdata_len = sizeof(retdata); 265 | ret = acrd_aio_read(h, "/tmp/0", retdata, &retdata_len, 0, 266 | ACRD_FLAG_SYNC, acb4); 267 | g_assert(ret == ACRD_SUCCESS); 268 | 269 | acrd_aio_flush(h); 270 | g_assert(res1 == ACRD_ERR_NOTFOUND); 271 | g_assert(res2 == ACRD_SUCCESS); 272 | g_assert(res3 == ACRD_SUCCESS); 273 | g_assert(res4 == ACRD_SUCCESS); 274 | acrd_aio_release(h, acb1); 275 | acrd_aio_release(h, acb2); 276 | acrd_aio_release(h, acb3); 277 | acrd_aio_release(h, acb4); 278 | 279 | g_assert(retdata_len == sizeof(data2)); 280 | g_assert_cmpstr(retdata, ==, data2); 281 | } 282 | 283 | static void test_aio_append(struct acrd_fixture *fixture, gconstpointer p) 284 | { 285 | struct acrd_handle *h = fixture->handle; 286 | const char data1[] = "data"; 287 | const char data2[] = "appended_data"; 288 | const char data3[] = "dataappended_data"; 289 | char retdata1[32], retdata2[32]; 290 | uint32_t retdata1_len, retdata2_len; 291 | int ret; 292 | struct acrd_aiocb *acb1, *acb2, *acb3, *acb4, *acb5; 293 | uint32_t res1, res2, res3, res4, res5; 294 | 295 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 296 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1) - 1, 0, ACRD_FLAG_CREATE, acb1); 297 | g_assert(ret == ACRD_SUCCESS); 298 | 299 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 300 | ret = acrd_aio_write(h, "/tmp/0", data2, sizeof(data2), 0, ACRD_FLAG_APPEND, acb2); 301 | g_assert(ret == ACRD_SUCCESS); 302 | 303 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 304 | retdata1_len = sizeof(retdata2); 305 | ret = acrd_aio_read(h, "/tmp/0", retdata1, &retdata1_len, 0, 0, acb3); 306 | g_assert(ret == ACRD_SUCCESS); 307 | 308 | acb4 = acrd_aio_setup(h, test_aio_cb, &res4); 309 | ret = acrd_aio_write(h, "/tmp/1", data2, sizeof(data2), 0, 310 | ACRD_FLAG_CREATE | ACRD_FLAG_APPEND, acb4); 311 | g_assert(ret == ACRD_SUCCESS); 312 | 313 | acb5 = acrd_aio_setup(h, test_aio_cb, &res5); 314 | retdata2_len = sizeof(retdata2); 315 | ret = acrd_aio_read(h, "/tmp/1", retdata2, &retdata2_len, 0, 0, acb5); 316 | g_assert(ret == ACRD_SUCCESS); 317 | 318 | acrd_aio_flush(h); 319 | g_assert(res1 == ACRD_SUCCESS); 320 | g_assert(res2 == ACRD_SUCCESS); 321 | g_assert(res3 == ACRD_SUCCESS); 322 | g_assert(res4 == ACRD_SUCCESS); 323 | g_assert(res5 == ACRD_SUCCESS); 324 | acrd_aio_release(h, acb1); 325 | acrd_aio_release(h, acb2); 326 | acrd_aio_release(h, acb3); 327 | acrd_aio_release(h, acb4); 328 | acrd_aio_release(h, acb5); 329 | 330 | g_assert(retdata1_len == sizeof(data3)); 331 | g_assert_cmpstr(retdata1, ==, data3); 332 | g_assert(retdata2_len == sizeof(data2)); 333 | g_assert_cmpstr(retdata2, ==, data2); 334 | } 335 | 336 | static void test_aio_exclusive(struct acrd_fixture *fixture, gconstpointer p) 337 | { 338 | struct acrd_handle *h = fixture->handle; 339 | const char data[] = "data"; 340 | int ret; 341 | struct acrd_aiocb *acb1, *acb2; 342 | uint32_t res1, res2; 343 | 344 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 345 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, 346 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL, acb1); 347 | g_assert(ret == ACRD_SUCCESS); 348 | 349 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 350 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, 351 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL, acb2); 352 | g_assert(ret == ACRD_SUCCESS); 353 | 354 | acrd_aio_flush(h); 355 | g_assert(res1 == ACRD_SUCCESS); 356 | g_assert(res2 == ACRD_ERR_EXIST); 357 | acrd_aio_release(h, acb1); 358 | acrd_aio_release(h, acb2); 359 | } 360 | 361 | static void test_aio_read(struct acrd_fixture *fixture, gconstpointer p) 362 | { 363 | struct acrd_handle *h = fixture->handle; 364 | const char data[] = "data"; 365 | char retdata[32]; 366 | uint32_t retdata_len; 367 | int ret; 368 | struct acrd_aiocb *acb1, *acb2, *acb3; 369 | uint32_t res1, res2, res3; 370 | 371 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 372 | retdata_len = sizeof(retdata); 373 | ret = acrd_aio_read(h, "/tmp/0", retdata, &retdata_len, 0, 0, acb1); 374 | g_assert(ret == ACRD_SUCCESS); 375 | 376 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 377 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb2); 378 | g_assert(ret == ACRD_SUCCESS); 379 | 380 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 381 | retdata_len = sizeof(retdata); 382 | ret = acrd_aio_read(h, "/tmp/0", retdata, &retdata_len, 0, 0, acb3); 383 | g_assert(ret == ACRD_SUCCESS); 384 | 385 | acrd_aio_flush(h); 386 | g_assert(res1 == ACRD_ERR_NOTFOUND); 387 | g_assert(res2 == ACRD_SUCCESS); 388 | g_assert(res3 == ACRD_SUCCESS); 389 | acrd_aio_release(h, acb1); 390 | acrd_aio_release(h, acb2); 391 | acrd_aio_release(h, acb3); 392 | 393 | g_assert(retdata_len == sizeof(data)); 394 | g_assert_cmpstr(retdata, ==, data); 395 | } 396 | 397 | static void test_aio_partial_read(struct acrd_fixture *fixture, gconstpointer p) 398 | { 399 | struct acrd_handle *h = fixture->handle; 400 | const char data[] = "data"; 401 | char retdata1[32], retdata2[32], retdata3[32]; 402 | uint32_t retdata1_len, retdata2_len, retdata3_len; 403 | int ret; 404 | struct acrd_aiocb *acb1, *acb2, *acb3, *acb4; 405 | uint32_t res1, res2, res3, res4; 406 | 407 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 408 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb1); 409 | g_assert(ret == ACRD_SUCCESS); 410 | 411 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 412 | retdata1_len = 2; 413 | ret = acrd_aio_read(h, "/tmp/0", retdata1, &retdata1_len, 1, 0, acb2); 414 | g_assert(ret == ACRD_SUCCESS); 415 | 416 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 417 | retdata2_len = sizeof(retdata2); 418 | ret = acrd_aio_read(h, "/tmp/0", retdata2, &retdata2_len, 3, 0, acb3); 419 | g_assert(ret == ACRD_SUCCESS); 420 | 421 | acb4 = acrd_aio_setup(h, test_aio_cb, &res4); 422 | retdata3_len = sizeof(retdata3); 423 | ret = acrd_aio_read(h, "/tmp/0", retdata3, &retdata3_len, 100, 0, acb4); 424 | g_assert(ret == ACRD_SUCCESS); 425 | 426 | acrd_aio_flush(h); 427 | g_assert(res1 == ACRD_SUCCESS); 428 | g_assert(res2 == ACRD_SUCCESS); 429 | g_assert(res3 == ACRD_SUCCESS); 430 | g_assert(res4 == ACRD_SUCCESS); 431 | acrd_aio_release(h, acb1); 432 | acrd_aio_release(h, acb2); 433 | acrd_aio_release(h, acb3); 434 | acrd_aio_release(h, acb4); 435 | 436 | g_assert(retdata1_len == 2); 437 | g_assert(retdata2_len == 2); 438 | g_assert(retdata3_len == 0); 439 | g_assert(memcmp(retdata1, "at", 2) == 0); 440 | g_assert_cmpstr(retdata2, ==, "a"); 441 | } 442 | 443 | static void test_aio_del(struct acrd_fixture *fixture, gconstpointer p) 444 | { 445 | struct acrd_handle *h = fixture->handle; 446 | const char data[] = "data"; 447 | int ret; 448 | struct acrd_aiocb *acb1, *acb2, *acb3; 449 | uint32_t res1, res2, res3; 450 | 451 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 452 | ret = acrd_aio_del(h, "/tmp/0", 0, acb1); 453 | g_assert(ret == ACRD_SUCCESS); 454 | 455 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 456 | ret = acrd_aio_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE, acb2); 457 | g_assert(ret == ACRD_SUCCESS); 458 | 459 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 460 | ret = acrd_aio_del(h, "/tmp/0", 0, acb3); 461 | g_assert(ret == ACRD_SUCCESS); 462 | 463 | acrd_aio_flush(h); 464 | g_assert(res1 == ACRD_ERR_NOTFOUND); 465 | g_assert(res2 == ACRD_SUCCESS); 466 | g_assert(res3 == ACRD_SUCCESS); 467 | acrd_aio_release(h, acb1); 468 | acrd_aio_release(h, acb2); 469 | acrd_aio_release(h, acb3); 470 | } 471 | 472 | static void test_aio_list(struct acrd_fixture *fixture, gconstpointer p) 473 | { 474 | struct acrd_handle *h = fixture->handle; 475 | LIST_HEAD(path_list); 476 | struct acrd_listcb listcb = { 477 | .cb = test_aio_list_cb, 478 | .arg = &path_list, 479 | }; 480 | struct acrd_path_list_entry *entry, *n; 481 | const char *data[] = {"data1", "data2", "data3", "data4", "data5"}; 482 | char path[64]; 483 | int i, ret; 484 | struct acrd_aiocb *acb; 485 | uint32_t res; 486 | 487 | for (i = 0; i < ARRAY_SIZE(data); i++) { 488 | sprintf(path, "/tmp/%d", i); 489 | ret = acrd_write(h, path, data[i], strlen(data[i]) + 1, 0, ACRD_FLAG_CREATE); 490 | g_assert(ret == ACRD_SUCCESS); 491 | } 492 | 493 | acb = acrd_aio_setup(h, test_aio_cb, &res); 494 | acrd_aio_list(h, NULL, 0, &listcb, acb); 495 | acrd_aio_wait(h, acb); 496 | i = 0; 497 | list_for_each_entry_safe(entry, n, &path_list, list) { 498 | if (strncmp(entry->path, "/tmp/", strlen("/tmp/")) != 0) { 499 | g_test_message("db is not clean"); 500 | continue; 501 | } 502 | g_assert(i < ARRAY_SIZE(data)); 503 | sprintf(path, "/tmp/%d", i); 504 | g_assert_cmpstr(entry->path, ==, path); 505 | 506 | free(entry->path); 507 | list_del(&entry->list); 508 | free(entry); 509 | i++; 510 | } 511 | 512 | g_assert(i == ARRAY_SIZE(data)); 513 | } 514 | 515 | static void test_aio_prefix_search(struct acrd_fixture *fixture, gconstpointer p) 516 | { 517 | struct acrd_handle *h = fixture->handle; 518 | LIST_HEAD(path_list); 519 | struct acrd_listcb listcb = { 520 | .cb = test_aio_list_cb, 521 | .arg = &path_list, 522 | }; 523 | struct acrd_path_list_entry *entry, *n; 524 | const char *data[] = {"data1", "data2", "data3", "data4", "data5"}; 525 | char path[64]; 526 | int i, ret; 527 | struct acrd_aiocb *acb; 528 | uint32_t res; 529 | 530 | ret = acrd_write(h, "/tmp/0", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 531 | ret = acrd_write(h, "/tmp/0a", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 532 | ret = acrd_write(h, "/tmp/0b", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 533 | ret = acrd_write(h, "/tmp/z", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 534 | ret = acrd_write(h, "/tmp/zc", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 535 | ret = acrd_write(h, "/tmp/zd", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 536 | g_assert(ret == ACRD_SUCCESS); 537 | for (i = 0; i < ARRAY_SIZE(data); i++) { 538 | sprintf(path, "/tmp/a%d", i); 539 | ret = acrd_write(h, path, data[i], strlen(data[i]) + 1, 0, ACRD_FLAG_CREATE); 540 | g_assert(ret == ACRD_SUCCESS); 541 | } 542 | 543 | acb = acrd_aio_setup(h, test_aio_cb, &res); 544 | acrd_aio_list(h, "/tmp/a", 0, &listcb, acb); 545 | acrd_aio_wait(h, acb); 546 | i = 0; 547 | list_for_each_entry_safe(entry, n, &path_list, list) { 548 | sprintf(path, "/tmp/a%d", i); 549 | g_assert_cmpstr(entry->path, ==, path); 550 | 551 | free(entry->path); 552 | list_del(&entry->list); 553 | free(entry); 554 | i++; 555 | } 556 | 557 | g_assert(i == ARRAY_SIZE(data)); 558 | } 559 | 560 | static void test_aio_copy(struct acrd_fixture *fixture, gconstpointer p) 561 | { 562 | struct acrd_handle *h = fixture->handle; 563 | const char data1[] = "data"; 564 | const char data2[] = "longer data"; 565 | char retdata1[32], retdata2[32]; 566 | uint32_t retdata1_len, retdata2_len; 567 | int ret; 568 | struct acrd_aiocb *acb1, *acb2, *acb3, *acb4, *acb5, *acb6, *acb7, *acb8, *acb9; 569 | uint32_t res1, res2, res3, res4, res5, res6, res7, res8, res9; 570 | 571 | acb1 = acrd_aio_setup(h, test_aio_cb, &res1); 572 | ret = acrd_aio_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE, acb1); 573 | g_assert(ret == ACRD_SUCCESS); 574 | 575 | acb2 = acrd_aio_setup(h, test_aio_cb, &res2); 576 | ret = acrd_aio_copy(h, "/tmp/0", "/tmp/1", 0, acb2); 577 | g_assert(ret == ACRD_SUCCESS); 578 | 579 | acb3 = acrd_aio_setup(h, test_aio_cb, &res3); 580 | ret = acrd_aio_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE, acb3); 581 | g_assert(ret == ACRD_SUCCESS); 582 | 583 | acb4 = acrd_aio_setup(h, test_aio_cb, &res4); 584 | ret = acrd_aio_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE, acb4); 585 | g_assert(ret == ACRD_SUCCESS); 586 | 587 | acb5 = acrd_aio_setup(h, test_aio_cb, &res5); 588 | ret = acrd_aio_copy(h, "/tmp/0", "/tmp/1", 589 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL, acb5); 590 | g_assert(ret == ACRD_SUCCESS); 591 | 592 | acb6 = acrd_aio_setup(h, test_aio_cb, &res6); 593 | retdata1_len = sizeof(retdata1); 594 | ret = acrd_aio_read(h, "/tmp/1", retdata1, &retdata1_len, 0, 0, acb6); 595 | g_assert(ret == ACRD_SUCCESS); 596 | 597 | acb7 = acrd_aio_setup(h, test_aio_cb, &res7); 598 | ret = acrd_aio_write(h, "/tmp/2", data2, sizeof(data2), 0, ACRD_FLAG_CREATE, acb7); 599 | g_assert(ret == ACRD_SUCCESS); 600 | 601 | acb8 = acrd_aio_setup(h, test_aio_cb, &res8); 602 | ret = acrd_aio_copy(h, "/tmp/0", "/tmp/2", 0, acb8); 603 | g_assert(ret == ACRD_SUCCESS); 604 | 605 | acb9 = acrd_aio_setup(h, test_aio_cb, &res9); 606 | retdata2_len = sizeof(retdata2); 607 | ret = acrd_aio_read(h, "/tmp/2", retdata2, &retdata2_len, 0, 0, acb9); 608 | g_assert(ret == ACRD_SUCCESS); 609 | 610 | acrd_aio_flush(h); 611 | g_assert(res1 == ACRD_SUCCESS); 612 | g_assert(res2 == ACRD_ERR_NOTFOUND); 613 | g_assert(res3 == ACRD_SUCCESS); 614 | g_assert(res4 == ACRD_SUCCESS); 615 | g_assert(res5 == ACRD_ERR_EXIST); 616 | g_assert(res6 == ACRD_SUCCESS); 617 | g_assert(res7 == ACRD_SUCCESS); 618 | g_assert(res8 == ACRD_SUCCESS); 619 | g_assert(res9 == ACRD_SUCCESS); 620 | acrd_aio_release(h, acb1); 621 | acrd_aio_release(h, acb2); 622 | acrd_aio_release(h, acb3); 623 | acrd_aio_release(h, acb4); 624 | acrd_aio_release(h, acb5); 625 | acrd_aio_release(h, acb6); 626 | acrd_aio_release(h, acb7); 627 | acrd_aio_release(h, acb8); 628 | acrd_aio_release(h, acb9); 629 | 630 | g_assert(retdata1_len == sizeof(data1)); 631 | g_assert_cmpstr(retdata1, ==, data1); 632 | g_assert(retdata2_len == sizeof(data1)); 633 | g_assert_cmpstr(retdata2, ==, data1); 634 | } 635 | 636 | int main(int argc, char **argv) 637 | { 638 | g_test_init(&argc, &argv, NULL); 639 | g_test_add("/aio/wait", struct acrd_fixture, NULL, 640 | test_aio_setup, test_aio_wait, test_aio_teardown); 641 | g_test_add("/aio/flush", struct acrd_fixture, NULL, 642 | test_aio_setup, test_aio_flush, test_aio_teardown); 643 | g_test_add("/aio/create", struct acrd_fixture, NULL, 644 | test_aio_setup, test_aio_create, test_aio_teardown); 645 | g_test_add("/aio/write", struct acrd_fixture, NULL, 646 | test_aio_setup, test_aio_write, test_aio_teardown); 647 | g_test_add("/aio/partial_write", struct acrd_fixture, NULL, 648 | test_aio_setup, test_aio_partial_write, test_aio_teardown); 649 | g_test_add("/aio/sync", struct acrd_fixture, NULL, 650 | test_aio_setup, test_aio_sync, test_aio_teardown); 651 | g_test_add("/aio/append", struct acrd_fixture, NULL, 652 | test_aio_setup, test_aio_append, test_aio_teardown); 653 | g_test_add("/aio/exclusive", struct acrd_fixture, NULL, 654 | test_aio_setup, test_aio_exclusive, test_aio_teardown); 655 | g_test_add("/aio/read", struct acrd_fixture, NULL, 656 | test_aio_setup, test_aio_read, test_aio_teardown); 657 | g_test_add("/aio/partial_read", struct acrd_fixture, NULL, 658 | test_aio_setup, test_aio_partial_read, test_aio_teardown); 659 | g_test_add("/aio/del", struct acrd_fixture, NULL, 660 | test_aio_setup, test_aio_del, test_aio_teardown); 661 | g_test_add("/aio/list", struct acrd_fixture, NULL, 662 | test_aio_setup, test_aio_list, test_aio_teardown); 663 | g_test_add("/aio/prefix_search", struct acrd_fixture, NULL, 664 | test_aio_setup, test_aio_prefix_search, test_aio_teardown); 665 | g_test_add("/aio/copy", struct acrd_fixture, NULL, 666 | test_aio_setup, test_aio_copy, test_aio_teardown); 667 | 668 | return g_test_run(); 669 | } 670 | -------------------------------------------------------------------------------- /test/test-concurrent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "accord.h" 17 | #include "util.h" 18 | #include "coroutine.h" 19 | 20 | #define NR_THREADS 100 21 | 22 | static pthread_t acrd_threads[NR_THREADS]; 23 | 24 | struct acrd_path_list_entry { 25 | char *path; 26 | 27 | struct list_head list; 28 | }; 29 | 30 | static void test_concurrent_list_cb(struct acrd_handle *h, const char *path, void *arg) 31 | { 32 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 33 | struct list_head *head = arg; 34 | 35 | entry->path = strdup(path); 36 | list_add_tail(&entry->list, head); 37 | } 38 | 39 | static void test_concurrent_setup(void) 40 | { 41 | struct acrd_handle *h; 42 | LIST_HEAD(path_list); 43 | struct acrd_listcb listcb = { 44 | .cb = test_concurrent_list_cb, 45 | .arg = &path_list, 46 | }; 47 | struct acrd_path_list_entry *entry, *n; 48 | 49 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 50 | g_assert(h != NULL); 51 | 52 | /* cleanup */ 53 | acrd_list(h, "/tmp/", 0, &listcb); 54 | list_for_each_entry_safe(entry, n, &path_list, list) { 55 | acrd_del(h, entry->path, 0); 56 | 57 | free(entry->path); 58 | list_del(&entry->list); 59 | free(entry); 60 | } 61 | 62 | acrd_close(h); 63 | } 64 | 65 | static pthread_mutex_t confchg_lock = PTHREAD_MUTEX_INITIALIZER; 66 | static pthread_cond_t confchg_cond = PTHREAD_COND_INITIALIZER; 67 | 68 | struct test_confchg_info { 69 | size_t nr_members; 70 | uint64_t members[NR_THREADS]; 71 | }; 72 | 73 | /* 74 | * check whether node id 'id' is included in 'members' 75 | */ 76 | static int is_member(uint64_t id, const uint64_t *members, size_t nr_members) 77 | { 78 | int i; 79 | 80 | for (i = 0; i < nr_members; i++) 81 | if (members[i] == id) 82 | return 1; 83 | 84 | return 0; 85 | } 86 | 87 | static void test_join_fn(struct acrd_handle *bh, const uint64_t *member_list, 88 | size_t member_list_entries, uint64_t nodeid, void *arg) 89 | { 90 | static int signaled; 91 | struct test_confchg_info *info = arg; 92 | int i; 93 | 94 | g_assert(member_list_entries <= NR_THREADS); 95 | 96 | if (info->nr_members != 0) { 97 | g_assert(info->nr_members + 1 == member_list_entries); 98 | g_assert(is_member(nodeid, member_list, member_list_entries)); 99 | for (i = 0; i < info->nr_members; i++) 100 | g_assert(is_member(info->members[i], member_list, 101 | member_list_entries)); 102 | } else 103 | /* there should be no duplication */ 104 | for (i = 0; i < member_list_entries; i++) 105 | g_assert(!is_member(member_list[i], member_list + i + 1, 106 | member_list_entries - i - 1)); 107 | 108 | info->nr_members = member_list_entries; 109 | memcpy(info->members, member_list, sizeof(uint64_t) * info->nr_members); 110 | 111 | if (member_list_entries == NR_THREADS) { 112 | pthread_mutex_lock(&confchg_lock); 113 | if (signaled == 0) { 114 | /* the first thread starts leaving tests */ 115 | signaled = 1; 116 | pthread_cond_signal(&confchg_cond); 117 | } 118 | pthread_mutex_unlock(&confchg_lock); 119 | } 120 | } 121 | 122 | static void test_leave_fn(struct acrd_handle *bh, const uint64_t *member_list, 123 | size_t member_list_entries, uint64_t nodeid, void *arg) 124 | { 125 | static int remaining_threads = NR_THREADS - 1; 126 | struct test_confchg_info *info = arg; 127 | int i; 128 | 129 | g_assert(0 <= member_list_entries); 130 | 131 | g_assert(info->nr_members - 1 == member_list_entries); 132 | g_assert(!is_member(nodeid, member_list, member_list_entries)); 133 | g_assert(is_member(nodeid, info->members, info->nr_members)); 134 | for (i = 0; i < member_list_entries; i++) 135 | g_assert(is_member(member_list[i], info->members, 136 | info->nr_members)); 137 | 138 | info->nr_members = member_list_entries; 139 | memcpy(info->members, member_list, sizeof(uint64_t) * info->nr_members); 140 | 141 | pthread_mutex_lock(&confchg_lock); 142 | if (remaining_threads == member_list_entries) { 143 | /* start stopping the next thread */ 144 | pthread_cond_signal(&confchg_cond); 145 | remaining_threads--; 146 | } 147 | pthread_mutex_unlock(&confchg_lock); 148 | } 149 | 150 | static void *test_confchg_fn(void *arg) 151 | { 152 | struct acrd_handle *h; 153 | struct test_confchg_info info = {0}; 154 | 155 | h = acrd_init("localhost", 9090, test_join_fn, test_leave_fn, &info); 156 | g_assert(h != NULL); 157 | 158 | pthread_mutex_lock(&confchg_lock); 159 | 160 | /* sleep until all the threads connect to the accord server */ 161 | pthread_cond_wait(&confchg_cond, &confchg_lock); 162 | 163 | pthread_mutex_unlock(&confchg_lock); 164 | 165 | acrd_close(h); 166 | 167 | pthread_exit(NULL); 168 | } 169 | 170 | static void test_concurrent_confchg(void) 171 | { 172 | int i; 173 | 174 | test_concurrent_setup(); 175 | 176 | for (i = 0; i < NR_THREADS; i++) 177 | pthread_create(acrd_threads + i, NULL, test_confchg_fn, NULL); 178 | 179 | for (i = 0; i < NR_THREADS; i++) 180 | pthread_join(acrd_threads[i], NULL); 181 | } 182 | 183 | static void *test_lock_fn(void *arg) 184 | { 185 | struct acrd_handle *h; 186 | const char data[] = "data"; 187 | int *ret = arg; 188 | 189 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 190 | g_assert(h != NULL); 191 | 192 | *ret = acrd_write(h, "/tmp/lock", data, sizeof(data), 0, 193 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 194 | 195 | acrd_close(h); 196 | 197 | pthread_exit(NULL); 198 | } 199 | 200 | static void test_concurrent_lock(void) 201 | { 202 | int i, nr_success = 0, nr_exists = 0; 203 | int ret[NR_THREADS]; 204 | 205 | test_concurrent_setup(); 206 | 207 | for (i = 0; i < NR_THREADS; i++) 208 | pthread_create(acrd_threads + i, NULL, test_lock_fn, ret + i); 209 | 210 | for (i = 0; i < NR_THREADS; i++) 211 | pthread_join(acrd_threads[i], NULL); 212 | 213 | for (i = 0; i < NR_THREADS; i++) { 214 | if (ret[i] == ACRD_SUCCESS) 215 | nr_success++; 216 | else if (ret[i] == ACRD_ERR_EXIST) 217 | nr_exists++; 218 | else 219 | g_assert_not_reached(); 220 | } 221 | 222 | /* only one thread can get a lock */ 223 | g_assert(nr_success == 1); 224 | g_assert(nr_exists == NR_THREADS - 1); 225 | } 226 | 227 | static int test_queue_push(struct acrd_handle *h) 228 | { 229 | struct acrd_tx *tx; 230 | char retdata[32]; 231 | uint32_t size, max; 232 | int ret; 233 | char path[256]; 234 | 235 | size = sizeof(retdata); 236 | ret = acrd_read(h, "/tmp/queue/max", retdata, &size, 0, 0); 237 | if (ret == ACRD_SUCCESS) { 238 | g_assert(size == sizeof(max)); 239 | 240 | memcpy(&max, retdata, sizeof(max)); 241 | g_assert(max > 0); 242 | 243 | sprintf(path, "/tmp/queue/%d", max + 1); 244 | 245 | tx = acrd_tx_init(h); 246 | acrd_tx_cmp(tx, "/tmp/queue/max", &max, sizeof(max), 0); 247 | max++; 248 | acrd_tx_write(tx, "/tmp/queue/max", &max, sizeof(max), 0, 0); 249 | acrd_tx_write(tx, path, &max, sizeof(max), 0, 250 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 251 | 252 | ret = acrd_tx_commit(tx, 0); 253 | 254 | acrd_tx_close(tx); 255 | } else if (ret == ACRD_ERR_NOTFOUND) { 256 | max = 1; 257 | 258 | sprintf(path, "/tmp/queue/%d", max); 259 | 260 | tx = acrd_tx_init(h); 261 | acrd_tx_write(tx, "/tmp/queue/min", &max, sizeof(max), 0, 262 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 263 | acrd_tx_write(tx, "/tmp/queue/max", &max, sizeof(max), 0, 264 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 265 | acrd_tx_write(tx, path, &max, sizeof(max), 0, 266 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 267 | 268 | ret = acrd_tx_commit(tx, 0); 269 | 270 | acrd_tx_close(tx); 271 | } else 272 | g_assert_not_reached(); 273 | 274 | if (ret != ACRD_SUCCESS) { 275 | g_assert(ret == ACRD_ERR_NOTEQUAL || ret == ACRD_ERR_EXIST); 276 | return -1; 277 | } 278 | 279 | return 0; 280 | } 281 | 282 | static int test_queue_pop(struct acrd_handle *h) 283 | { 284 | struct acrd_tx *tx; 285 | char retdata[32]; 286 | uint32_t size, min; 287 | int ret; 288 | char path[256]; 289 | 290 | size = sizeof(retdata); 291 | ret = acrd_read(h, "/tmp/queue/min", retdata, &size, 0, 0); 292 | g_assert(ret == ACRD_SUCCESS); 293 | g_assert(size == sizeof(min)); 294 | 295 | memcpy(&min, retdata, sizeof(min)); 296 | g_assert(min > 0); 297 | 298 | sprintf(path, "/tmp/queue/%d", min); 299 | 300 | tx = acrd_tx_init(h); 301 | acrd_tx_cmp(tx, "/tmp/queue/min", &min, sizeof(min), 0); 302 | min++; 303 | acrd_tx_write(tx, "/tmp/queue/min", &min, sizeof(min), 0, 0); 304 | acrd_tx_del(tx, path, 0); 305 | 306 | ret = acrd_tx_commit(tx, 0); 307 | 308 | acrd_tx_close(tx); 309 | 310 | if (ret != ACRD_SUCCESS) { 311 | g_assert(ret == ACRD_ERR_NOTEQUAL); 312 | return -1; 313 | } 314 | 315 | return 0; 316 | } 317 | 318 | static void *test_queue_fn(void *arg) 319 | { 320 | struct acrd_handle *h; 321 | 322 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 323 | g_assert(h != NULL); 324 | 325 | while (test_queue_push(h) != 0) 326 | ; 327 | 328 | while (test_queue_pop(h) != 0) 329 | ; 330 | 331 | while (test_queue_push(h) != 0) 332 | ; 333 | 334 | while (test_queue_pop(h) != 0) 335 | ; 336 | 337 | acrd_close(h); 338 | 339 | pthread_exit(NULL); 340 | } 341 | 342 | static void test_concurrent_queue(void) 343 | { 344 | struct acrd_handle *h; 345 | int i, ret; 346 | char retdata[32]; 347 | uint32_t size, min, max; 348 | 349 | test_concurrent_setup(); 350 | 351 | for (i = 0; i < NR_THREADS; i++) 352 | pthread_create(acrd_threads + i, NULL, test_queue_fn, NULL); 353 | 354 | for (i = 0; i < NR_THREADS; i++) 355 | pthread_join(acrd_threads[i], NULL); 356 | 357 | 358 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 359 | g_assert(h != NULL); 360 | 361 | size = sizeof(retdata); 362 | ret = acrd_read(h, "/tmp/queue/max", retdata, &size, 0, 0); 363 | g_assert(ret == ACRD_SUCCESS); 364 | g_assert(size == sizeof(max)); 365 | 366 | memcpy(&max, retdata, sizeof(max)); 367 | 368 | size = sizeof(retdata); 369 | ret = acrd_read(h, "/tmp/queue/max", retdata, &size, 0, 0); 370 | g_assert(ret == ACRD_SUCCESS); 371 | g_assert(size == sizeof(min)); 372 | 373 | memcpy(&min, retdata, sizeof(min)); 374 | 375 | g_assert(max == 2 * NR_THREADS); 376 | g_assert(min == 2 * NR_THREADS); 377 | 378 | acrd_close(h); 379 | } 380 | 381 | int main(int argc, char **argv) 382 | { 383 | g_test_init(&argc, &argv, NULL); 384 | g_test_add_func("/concurrent/confchg", test_concurrent_confchg); 385 | g_test_add_func("/concurrent/lock", test_concurrent_lock); 386 | g_test_add_func("/concurrent/queue", test_concurrent_queue); 387 | 388 | return g_test_run(); 389 | } 390 | -------------------------------------------------------------------------------- /test/test-coroutine.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | * 11 | * This code is based on test-coroutine.c from QEMU: 12 | * Copyright (C) 2011 Stefan Hajnoczi 13 | */ 14 | 15 | #include 16 | #include "coroutine.h" 17 | 18 | /* 19 | * Check that in_coroutine() works 20 | */ 21 | 22 | static void verify_in_coroutine(void *opaque) 23 | { 24 | g_assert(in_coroutine()); 25 | } 26 | 27 | static void test_in_coroutine(void) 28 | { 29 | struct coroutine *coroutine; 30 | 31 | g_assert(!in_coroutine()); 32 | 33 | coroutine = coroutine_create(verify_in_coroutine); 34 | coroutine_enter(coroutine, NULL); 35 | } 36 | 37 | /* 38 | * Check that coroutine_self() works 39 | */ 40 | 41 | static void verify_self(void *opaque) 42 | { 43 | g_assert(coroutine_self() == opaque); 44 | } 45 | 46 | static void test_self(void) 47 | { 48 | struct coroutine *coroutine; 49 | 50 | coroutine = coroutine_create(verify_self); 51 | coroutine_enter(coroutine, coroutine); 52 | } 53 | 54 | /* 55 | * Check that coroutines may nest multiple levels 56 | */ 57 | 58 | struct nest_data { 59 | unsigned int n_enter; /* num coroutines entered */ 60 | unsigned int n_return; /* num coroutines returned */ 61 | unsigned int max; /* maximum level of nesting */ 62 | }; 63 | 64 | static void nest(void *opaque) 65 | { 66 | struct nest_data *nd = opaque; 67 | 68 | nd->n_enter++; 69 | 70 | if (nd->n_enter < nd->max) { 71 | struct coroutine *child; 72 | 73 | child = coroutine_create(nest); 74 | coroutine_enter(child, nd); 75 | } 76 | 77 | nd->n_return++; 78 | } 79 | 80 | static void test_nesting(void) 81 | { 82 | struct coroutine *root; 83 | struct nest_data nd = { 84 | .n_enter = 0, 85 | .n_return = 0, 86 | .max = 128, 87 | }; 88 | 89 | root = coroutine_create(nest); 90 | coroutine_enter(root, &nd); 91 | 92 | /* Must enter and return from max nesting level */ 93 | g_assert_cmpint(nd.n_enter, ==, nd.max); 94 | g_assert_cmpint(nd.n_return, ==, nd.max); 95 | } 96 | 97 | /* 98 | * Check that yield/enter transfer control correctly 99 | */ 100 | 101 | static void yield_5_times(void *opaque) 102 | { 103 | int *done = opaque; 104 | int i; 105 | 106 | for (i = 0; i < 5; i++) 107 | coroutine_yield(); 108 | 109 | *done = 1; 110 | } 111 | 112 | static void test_yield(void) 113 | { 114 | struct coroutine *coroutine; 115 | int done = 0; 116 | int i = -1; /* one extra time to return from coroutine */ 117 | 118 | coroutine = coroutine_create(yield_5_times); 119 | while (!done) { 120 | coroutine_enter(coroutine, &done); 121 | i++; 122 | } 123 | g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ 124 | } 125 | 126 | /* 127 | * Check that creation, enter, and return work 128 | */ 129 | 130 | static void set_and_exit(void *opaque) 131 | { 132 | int *done = opaque; 133 | 134 | *done = 1; 135 | } 136 | 137 | static void test_lifecycle(void) 138 | { 139 | struct coroutine *coroutine; 140 | int done = 0; 141 | 142 | /* Create, enter, and return from coroutine */ 143 | coroutine = coroutine_create(set_and_exit); 144 | coroutine_enter(coroutine, &done); 145 | g_assert(done); /* expect done to be true (first time) */ 146 | 147 | /* Repeat to check that no state affects this test */ 148 | done = 0; 149 | coroutine = coroutine_create(set_and_exit); 150 | coroutine_enter(coroutine, &done); 151 | g_assert(done); /* expect done to be true (second time) */ 152 | } 153 | 154 | /* 155 | * Lifecycle benchmark 156 | */ 157 | 158 | static void empty_coroutine(void *opaque) 159 | { 160 | /* Do nothing */ 161 | } 162 | 163 | static void perf_lifecycle(void) 164 | { 165 | struct coroutine *coroutine; 166 | unsigned int i, max; 167 | double duration; 168 | 169 | max = 1000000; 170 | 171 | g_test_timer_start(); 172 | for (i = 0; i < max; i++) { 173 | coroutine = coroutine_create(empty_coroutine); 174 | coroutine_enter(coroutine, NULL); 175 | } 176 | duration = g_test_timer_elapsed(); 177 | 178 | g_test_message("Lifecycle %u iterations: %f s\n", max, duration); 179 | } 180 | 181 | int main(int argc, char **argv) 182 | { 183 | g_test_init(&argc, &argv, NULL); 184 | g_test_add_func("/coroutine/lifecycle", test_lifecycle); 185 | g_test_add_func("/coroutine/yield", test_yield); 186 | g_test_add_func("/coroutine/nesting", test_nesting); 187 | g_test_add_func("/coroutine/self", test_self); 188 | g_test_add_func("/coroutine/in_coroutine", test_in_coroutine); 189 | if (g_test_perf()) 190 | g_test_add_func("/perf/lifecycle", perf_lifecycle); 191 | 192 | return g_test_run(); 193 | } 194 | -------------------------------------------------------------------------------- /test/test-io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | #include "util.h" 17 | #include "list.h" 18 | 19 | struct acrd_fixture { 20 | struct acrd_handle *handle; 21 | }; 22 | 23 | struct acrd_path_list_entry { 24 | char *path; 25 | 26 | struct list_head list; 27 | }; 28 | 29 | static void test_io_list_cb(struct acrd_handle *h, const char *path, void *arg) 30 | { 31 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 32 | struct list_head *head = arg; 33 | 34 | entry->path = strdup(path); 35 | list_add_tail(&entry->list, head); 36 | } 37 | 38 | static void test_io_setup(struct acrd_fixture *fixture, gconstpointer p) 39 | { 40 | struct acrd_handle *h; 41 | LIST_HEAD(path_list); 42 | struct acrd_listcb listcb = { 43 | .cb = test_io_list_cb, 44 | .arg = &path_list, 45 | }; 46 | struct acrd_path_list_entry *entry, *n; 47 | 48 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 49 | g_assert(h != NULL); 50 | 51 | /* cleanup */ 52 | acrd_list(h, "/tmp/", 0, &listcb); 53 | list_for_each_entry_safe(entry, n, &path_list, list) { 54 | acrd_del(h, entry->path, 0); 55 | 56 | free(entry->path); 57 | list_del(&entry->list); 58 | free(entry); 59 | } 60 | 61 | fixture->handle = h; 62 | } 63 | 64 | static void test_io_teardown(struct acrd_fixture *fixture, gconstpointer p) 65 | { 66 | acrd_close(fixture->handle); 67 | } 68 | 69 | static void test_io_create(struct acrd_fixture *fixture, gconstpointer p) 70 | { 71 | struct acrd_handle *h = fixture->handle; 72 | const char data[] = "data"; 73 | int ret; 74 | 75 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 76 | g_assert(ret == ACRD_SUCCESS); 77 | 78 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 79 | g_assert(ret == ACRD_SUCCESS); 80 | } 81 | 82 | static void test_io_write(struct acrd_fixture *fixture, gconstpointer p) 83 | { 84 | struct acrd_handle *h = fixture->handle; 85 | const char data1[] = "data"; 86 | const char data2[] = "newdata"; 87 | char retdata[32]; 88 | uint32_t retdata_len; 89 | int ret; 90 | 91 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, 0); 92 | g_assert(ret == ACRD_ERR_NOTFOUND); 93 | 94 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE); 95 | g_assert(ret == ACRD_SUCCESS); 96 | 97 | ret = acrd_write(h, "/tmp/0", data2, sizeof(data2), 0, 0); 98 | g_assert(ret == ACRD_SUCCESS); 99 | 100 | retdata_len = sizeof(retdata); 101 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 102 | g_assert(ret == ACRD_SUCCESS); 103 | g_assert(retdata_len == sizeof(data2)); 104 | g_assert_cmpstr(retdata, ==, data2); 105 | } 106 | 107 | static void test_io_partial_write(struct acrd_fixture *fixture, gconstpointer p) 108 | { 109 | struct acrd_handle *h = fixture->handle; 110 | const char data[] = "data"; 111 | char retdata[32]; 112 | uint32_t retdata_len; 113 | int ret; 114 | 115 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 116 | g_assert(ret == ACRD_SUCCESS); 117 | 118 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 2, 0); 119 | g_assert(ret == ACRD_SUCCESS); 120 | 121 | retdata_len = sizeof(retdata); 122 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 123 | g_assert(ret == ACRD_SUCCESS); 124 | g_assert(retdata_len == 7); 125 | g_assert_cmpstr(retdata, ==, "dadata"); 126 | } 127 | 128 | static void test_io_sync(struct acrd_fixture *fixture, gconstpointer p) 129 | { 130 | struct acrd_handle *h = fixture->handle; 131 | const char data1[] = "data"; 132 | const char data2[] = "newdata"; 133 | char retdata[32]; 134 | uint32_t retdata_len; 135 | int ret; 136 | 137 | ret = acrd_write(h, "/tmp/0", data2, sizeof(data2), 0, ACRD_FLAG_SYNC); 138 | g_assert(ret == ACRD_ERR_NOTFOUND); 139 | 140 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, 141 | ACRD_FLAG_CREATE | ACRD_FLAG_SYNC); 142 | g_assert(ret == ACRD_SUCCESS); 143 | 144 | ret = acrd_write(h, "/tmp/0", data2, sizeof(data2), 0, ACRD_FLAG_SYNC); 145 | g_assert(ret == ACRD_SUCCESS); 146 | 147 | retdata_len = sizeof(retdata); 148 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, ACRD_FLAG_SYNC); 149 | g_assert(ret == ACRD_SUCCESS); 150 | g_assert(retdata_len == sizeof(data2)); 151 | g_assert_cmpstr(retdata, ==, data2); 152 | } 153 | 154 | static void test_io_append(struct acrd_fixture *fixture, gconstpointer p) 155 | { 156 | struct acrd_handle *h = fixture->handle; 157 | const char data1[] = "data"; 158 | const char data2[] = "appended_data"; 159 | const char data3[] = "dataappended_data"; 160 | char retdata[32]; 161 | uint32_t retdata_len; 162 | int ret; 163 | 164 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1) - 1, 0, ACRD_FLAG_CREATE); 165 | g_assert(ret == ACRD_SUCCESS); 166 | 167 | ret = acrd_write(h, "/tmp/0", data2, sizeof(data2), 0, ACRD_FLAG_APPEND); 168 | g_assert(ret == ACRD_SUCCESS); 169 | 170 | retdata_len = sizeof(retdata); 171 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 172 | g_assert(ret == ACRD_SUCCESS); 173 | g_assert(retdata_len == sizeof(data3)); 174 | g_assert_cmpstr(retdata, ==, data3); 175 | 176 | ret = acrd_write(h, "/tmp/1", data2, sizeof(data2), 0, 177 | ACRD_FLAG_CREATE | ACRD_FLAG_APPEND); 178 | g_assert(ret == ACRD_SUCCESS); 179 | 180 | retdata_len = sizeof(retdata); 181 | ret = acrd_read(h, "/tmp/1", retdata, &retdata_len, 0, 0); 182 | g_assert(ret == ACRD_SUCCESS); 183 | g_assert(retdata_len == sizeof(data2)); 184 | g_assert_cmpstr(retdata, ==, data2); 185 | } 186 | 187 | static void test_io_exclusive(struct acrd_fixture *fixture, gconstpointer p) 188 | { 189 | struct acrd_handle *h = fixture->handle; 190 | const char data[] = "data"; 191 | int ret; 192 | 193 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, 194 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 195 | g_assert(ret == ACRD_SUCCESS); 196 | 197 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, 198 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 199 | g_assert(ret == ACRD_ERR_EXIST); 200 | } 201 | 202 | static void test_io_read(struct acrd_fixture *fixture, gconstpointer p) 203 | { 204 | struct acrd_handle *h = fixture->handle; 205 | const char data[] = "data"; 206 | char retdata[32]; 207 | uint32_t retdata_len; 208 | int ret; 209 | 210 | retdata_len = sizeof(retdata); 211 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 212 | g_assert(ret == ACRD_ERR_NOTFOUND); 213 | 214 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 215 | g_assert(ret == ACRD_SUCCESS); 216 | 217 | retdata_len = sizeof(retdata); 218 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 219 | g_assert(ret == ACRD_SUCCESS); 220 | g_assert(retdata_len == sizeof(data)); 221 | g_assert_cmpstr(retdata, ==, data); 222 | } 223 | 224 | static void test_io_partial_read(struct acrd_fixture *fixture, gconstpointer p) 225 | { 226 | struct acrd_handle *h = fixture->handle; 227 | const char data[] = "data"; 228 | char retdata[32]; 229 | uint32_t retdata_len; 230 | int ret; 231 | 232 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 233 | g_assert(ret == ACRD_SUCCESS); 234 | 235 | retdata_len = 2; 236 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 1, 0); 237 | g_assert(ret == ACRD_SUCCESS); 238 | g_assert(retdata_len == 2); 239 | g_assert(memcmp(retdata, "at", 2) == 0); 240 | 241 | retdata_len = sizeof(retdata); 242 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 3, 0); 243 | g_assert(ret == ACRD_SUCCESS); 244 | g_assert(retdata_len == 2); 245 | g_assert_cmpstr(retdata, ==, "a"); 246 | 247 | retdata_len = sizeof(retdata); 248 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 100, 0); 249 | g_assert(ret == ACRD_SUCCESS); 250 | g_assert(retdata_len == 0); 251 | } 252 | 253 | static void test_io_del(struct acrd_fixture *fixture, gconstpointer p) 254 | { 255 | struct acrd_handle *h = fixture->handle; 256 | const char data[] = "data"; 257 | int ret; 258 | 259 | ret = acrd_del(h, "/tmp/0", 0); 260 | g_assert(ret == ACRD_ERR_NOTFOUND); 261 | 262 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 263 | g_assert(ret == ACRD_SUCCESS); 264 | 265 | ret = acrd_del(h, "/tmp/0", 0); 266 | g_assert(ret == ACRD_SUCCESS); 267 | } 268 | 269 | static void test_io_list(struct acrd_fixture *fixture, gconstpointer p) 270 | { 271 | struct acrd_handle *h = fixture->handle; 272 | LIST_HEAD(path_list); 273 | struct acrd_listcb listcb = { 274 | .cb = test_io_list_cb, 275 | .arg = &path_list, 276 | }; 277 | struct acrd_path_list_entry *entry, *n; 278 | const char *data[] = {"data1", "data2", "data3", "data4", "data5"}; 279 | char path[64]; 280 | int i, ret; 281 | 282 | for (i = 0; i < ARRAY_SIZE(data); i++) { 283 | sprintf(path, "/tmp/%d", i); 284 | ret = acrd_write(h, path, data[i], strlen(data[i]) + 1, 0, 285 | ACRD_FLAG_CREATE); 286 | g_assert(ret == ACRD_SUCCESS); 287 | } 288 | 289 | acrd_list(h, NULL, 0, &listcb); 290 | i = 0; 291 | list_for_each_entry_safe(entry, n, &path_list, list) { 292 | if (strncmp(entry->path, "/tmp/", strlen("/tmp/")) != 0) { 293 | g_test_message("db is not clean"); 294 | continue; 295 | } 296 | g_assert(i < ARRAY_SIZE(data)); 297 | sprintf(path, "/tmp/%d", i); 298 | g_assert_cmpstr(entry->path, ==, path); 299 | 300 | free(entry->path); 301 | list_del(&entry->list); 302 | free(entry); 303 | i++; 304 | } 305 | 306 | g_assert(i == ARRAY_SIZE(data)); 307 | } 308 | 309 | static void test_io_prefix_search(struct acrd_fixture *fixture, gconstpointer p) 310 | { 311 | struct acrd_handle *h = fixture->handle; 312 | LIST_HEAD(path_list); 313 | struct acrd_listcb listcb = { 314 | .cb = test_io_list_cb, 315 | .arg = &path_list, 316 | }; 317 | struct acrd_path_list_entry *entry, *n; 318 | const char *data[] = {"data1", "data2", "data3", "data4", "data5"}; 319 | char path[64]; 320 | int i, ret; 321 | 322 | ret = acrd_write(h, "/tmp/0", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 323 | ret = acrd_write(h, "/tmp/0a", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 324 | ret = acrd_write(h, "/tmp/0b", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 325 | ret = acrd_write(h, "/tmp/z", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 326 | ret = acrd_write(h, "/tmp/zc", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 327 | ret = acrd_write(h, "/tmp/zd", data[0], strlen(data[0]) + 1, 0, ACRD_FLAG_CREATE); 328 | g_assert(ret == ACRD_SUCCESS); 329 | for (i = 0; i < ARRAY_SIZE(data); i++) { 330 | sprintf(path, "/tmp/a%d", i); 331 | ret = acrd_write(h, path, data[i], strlen(data[i]) + 1, 0, ACRD_FLAG_CREATE); 332 | g_assert(ret == ACRD_SUCCESS); 333 | } 334 | 335 | acrd_list(h, "/tmp/a", 0, &listcb); 336 | i = 0; 337 | list_for_each_entry_safe(entry, n, &path_list, list) { 338 | sprintf(path, "/tmp/a%d", i); 339 | g_assert_cmpstr(entry->path, ==, path); 340 | 341 | free(entry->path); 342 | list_del(&entry->list); 343 | free(entry); 344 | i++; 345 | } 346 | 347 | g_assert(i == ARRAY_SIZE(data)); 348 | } 349 | 350 | static void test_io_copy(struct acrd_fixture *fixture, gconstpointer p) 351 | { 352 | struct acrd_handle *h = fixture->handle; 353 | const char data1[] = "data"; 354 | const char data2[] = "longer data"; 355 | char retdata[32]; 356 | uint32_t retdata_len; 357 | int ret; 358 | 359 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE); 360 | g_assert(ret == ACRD_SUCCESS); 361 | 362 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", 0); 363 | g_assert(ret == ACRD_ERR_NOTFOUND); 364 | 365 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE); 366 | g_assert(ret == ACRD_SUCCESS); 367 | 368 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE); 369 | g_assert(ret == ACRD_SUCCESS); 370 | 371 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 372 | g_assert(ret == ACRD_ERR_EXIST); 373 | 374 | retdata_len = sizeof(retdata); 375 | ret = acrd_read(h, "/tmp/1", retdata, &retdata_len, 0, 0); 376 | g_assert(ret == ACRD_SUCCESS); 377 | g_assert(retdata_len == sizeof(data1)); 378 | g_assert_cmpstr(retdata, ==, data1); 379 | 380 | ret = acrd_write(h, "/tmp/2", data2, sizeof(data2), 0, ACRD_FLAG_CREATE); 381 | g_assert(ret == ACRD_SUCCESS); 382 | 383 | ret = acrd_copy(h, "/tmp/0", "/tmp/2", 0); 384 | g_assert(ret == ACRD_SUCCESS); 385 | 386 | retdata_len = sizeof(retdata); 387 | ret = acrd_read(h, "/tmp/2", retdata, &retdata_len, 0, 0); 388 | g_assert(ret == ACRD_SUCCESS); 389 | g_assert(retdata_len == sizeof(data1)); 390 | g_assert_cmpstr(retdata, ==, data1); 391 | } 392 | 393 | static void test_io_large_data(struct acrd_fixture *fixture, gconstpointer p) 394 | { 395 | struct acrd_handle *h = fixture->handle; 396 | uint8_t *data1, *data2; 397 | uint32_t retdata_len; 398 | int ret; 399 | const int data_size = 32 * 1024 * 1024; /* 32 MB */ 400 | 401 | data1 = malloc(data_size); 402 | g_assert(data1); 403 | data2 = malloc(data_size); 404 | g_assert(data2); 405 | 406 | memset(data1, 0x5a, data_size); 407 | memset(data2, 0x00, data_size); 408 | 409 | ret = acrd_write(h, "/tmp/0", data1, data_size, 0, ACRD_FLAG_CREATE); 410 | g_assert(ret == ACRD_SUCCESS); 411 | 412 | retdata_len = data_size; 413 | ret = acrd_read(h, "/tmp/0", data2, &retdata_len, 0, 0); 414 | g_assert(ret == ACRD_SUCCESS); 415 | g_assert(retdata_len == data_size); 416 | g_assert(memcmp(data1, data2, data_size) == 0); 417 | 418 | free(data1); 419 | free(data2); 420 | } 421 | 422 | int main(int argc, char **argv) 423 | { 424 | g_test_init(&argc, &argv, NULL); 425 | g_test_add("/io/create", struct acrd_fixture, NULL, 426 | test_io_setup, test_io_create, test_io_teardown); 427 | g_test_add("/io/write", struct acrd_fixture, NULL, 428 | test_io_setup, test_io_write, test_io_teardown); 429 | g_test_add("/io/partial_write", struct acrd_fixture, NULL, 430 | test_io_setup, test_io_partial_write, test_io_teardown); 431 | g_test_add("/io/sync", struct acrd_fixture, NULL, 432 | test_io_setup, test_io_sync, test_io_teardown); 433 | g_test_add("/io/append", struct acrd_fixture, NULL, 434 | test_io_setup, test_io_append, test_io_teardown); 435 | g_test_add("/io/exclusive", struct acrd_fixture, NULL, 436 | test_io_setup, test_io_exclusive, test_io_teardown); 437 | g_test_add("/io/read", struct acrd_fixture, NULL, 438 | test_io_setup, test_io_read, test_io_teardown); 439 | g_test_add("/io/partial_read", struct acrd_fixture, NULL, 440 | test_io_setup, test_io_partial_read, test_io_teardown); 441 | g_test_add("/io/del", struct acrd_fixture, NULL, 442 | test_io_setup, test_io_del, test_io_teardown); 443 | g_test_add("/io/list", struct acrd_fixture, NULL, 444 | test_io_setup, test_io_list, test_io_teardown); 445 | g_test_add("/io/prefix_search", struct acrd_fixture, NULL, 446 | test_io_setup, test_io_prefix_search, test_io_teardown); 447 | g_test_add("/io/copy", struct acrd_fixture, NULL, 448 | test_io_setup, test_io_copy, test_io_teardown); 449 | g_test_add("/io/large_data", struct acrd_fixture, NULL, 450 | test_io_setup, test_io_large_data, test_io_teardown); 451 | 452 | return g_test_run(); 453 | } 454 | -------------------------------------------------------------------------------- /test/test-txn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | #include "util.h" 17 | #include "list.h" 18 | 19 | struct acrd_fixture { 20 | struct acrd_handle *handle; 21 | }; 22 | 23 | struct acrd_path_list_entry { 24 | char *path; 25 | 26 | struct list_head list; 27 | }; 28 | 29 | static void test_txn_list_cb(struct acrd_handle *h, const char *path, void *arg) 30 | { 31 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 32 | struct list_head *head = arg; 33 | 34 | entry->path = strdup(path); 35 | list_add_tail(&entry->list, head); 36 | } 37 | 38 | static void test_txn_setup(struct acrd_fixture *fixture, gconstpointer p) 39 | { 40 | struct acrd_handle *h; 41 | LIST_HEAD(path_list); 42 | struct acrd_listcb listcb = { 43 | .cb = test_txn_list_cb, 44 | .arg = &path_list, 45 | }; 46 | struct acrd_path_list_entry *entry, *n; 47 | 48 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 49 | g_assert(h != NULL); 50 | 51 | /* cleanup */ 52 | acrd_list(h, "/tmp/", 0, &listcb); 53 | list_for_each_entry_safe(entry, n, &path_list, list) { 54 | acrd_del(h, entry->path, 0); 55 | 56 | free(entry->path); 57 | list_del(&entry->list); 58 | free(entry); 59 | } 60 | 61 | fixture->handle = h; 62 | } 63 | 64 | static void test_txn_teardown(struct acrd_fixture *fixture, gconstpointer p) 65 | { 66 | acrd_close(fixture->handle); 67 | } 68 | 69 | static void test_txn_multi_write(struct acrd_fixture *fixture, gconstpointer p) 70 | { 71 | struct acrd_handle *h = fixture->handle; 72 | const char data[] = "data"; 73 | char retdata[32]; 74 | uint32_t retdata_len; 75 | int ret; 76 | struct acrd_tx *tx; 77 | 78 | tx = acrd_tx_init(h); 79 | g_assert(tx != NULL); 80 | ret = acrd_tx_write(tx, "/tmp/0", data, sizeof(data), 0, 81 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 82 | g_assert(ret == ACRD_SUCCESS); 83 | ret = acrd_tx_write(tx, "/tmp/1", data, sizeof(data), 0, 84 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 85 | g_assert(ret == ACRD_SUCCESS); 86 | ret = acrd_tx_commit(tx, 0); 87 | g_assert(ret == ACRD_SUCCESS); 88 | acrd_tx_close(tx); 89 | 90 | retdata_len = sizeof(retdata); 91 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 92 | g_assert(ret == ACRD_SUCCESS); 93 | g_assert(retdata_len == sizeof(data)); 94 | g_assert_cmpstr(retdata, ==, data); 95 | retdata_len = sizeof(retdata); 96 | ret = acrd_read(h, "/tmp/1", retdata, &retdata_len, 0, 0); 97 | g_assert(ret == ACRD_SUCCESS); 98 | g_assert(retdata_len == sizeof(data)); 99 | g_assert_cmpstr(retdata, ==, data); 100 | 101 | tx = acrd_tx_init(h); 102 | g_assert(tx != NULL); 103 | ret = acrd_tx_write(tx, "/tmp/0", data, sizeof(data), 0, 104 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 105 | g_assert(ret == ACRD_SUCCESS); 106 | ret = acrd_tx_write(tx, "/tmp/2", data, sizeof(data), 0, 107 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 108 | g_assert(ret == ACRD_SUCCESS); 109 | ret = acrd_tx_commit(tx, 0); 110 | g_assert(ret == ACRD_ERR_EXIST); 111 | acrd_tx_close(tx); 112 | 113 | tx = acrd_tx_init(h); 114 | g_assert(tx != NULL); 115 | ret = acrd_tx_write(tx, "/tmp/2", data, sizeof(data), 0, 116 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 117 | g_assert(ret == ACRD_SUCCESS); 118 | ret = acrd_tx_write(tx, "/tmp/1", data, sizeof(data), 0, 119 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 120 | g_assert(ret == ACRD_SUCCESS); 121 | ret = acrd_tx_commit(tx, 0); 122 | g_assert(ret == ACRD_ERR_EXIST); 123 | acrd_tx_close(tx); 124 | 125 | tx = acrd_tx_init(h); 126 | g_assert(tx != NULL); 127 | ret = acrd_tx_write(tx, "/tmp/3", data, sizeof(data), 0, 128 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 129 | g_assert(ret == ACRD_SUCCESS); 130 | ret = acrd_tx_write(tx, "/tmp/3", data, sizeof(data), 0, 131 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 132 | g_assert(ret == ACRD_SUCCESS); 133 | ret = acrd_tx_commit(tx, 0); 134 | g_assert(ret == ACRD_ERR_EXIST); 135 | acrd_tx_close(tx); 136 | } 137 | 138 | static void test_txn_multi_read(struct acrd_fixture *fixture, gconstpointer p) 139 | { 140 | struct acrd_handle *h = fixture->handle; 141 | const char data[] = "data"; 142 | char retdata1[32], retdata2[32]; 143 | uint32_t retdata1_len, retdata2_len; 144 | int ret; 145 | struct acrd_tx *tx; 146 | 147 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 148 | g_assert(ret == ACRD_SUCCESS); 149 | ret = acrd_write(h, "/tmp/1", data, sizeof(data), 0, ACRD_FLAG_CREATE); 150 | g_assert(ret == ACRD_SUCCESS); 151 | 152 | tx = acrd_tx_init(h); 153 | g_assert(tx != NULL); 154 | retdata1_len = sizeof(retdata1); 155 | ret = acrd_tx_read(tx, "/tmp/0", retdata1, &retdata1_len, 0, 0); 156 | g_assert(ret == ACRD_SUCCESS); 157 | retdata2_len = sizeof(retdata2); 158 | ret = acrd_tx_read(tx, "/tmp/1", retdata2, &retdata2_len, 0, 0); 159 | g_assert(ret == ACRD_SUCCESS); 160 | ret = acrd_tx_commit(tx, 0); 161 | g_assert(ret == ACRD_SUCCESS); 162 | acrd_tx_close(tx); 163 | 164 | g_assert(retdata1_len == sizeof(data)); 165 | g_assert_cmpstr(data, ==, retdata1); 166 | g_assert(retdata2_len == sizeof(data)); 167 | g_assert_cmpstr(data, ==, retdata2); 168 | 169 | tx = acrd_tx_init(h); 170 | g_assert(tx != NULL); 171 | retdata2_len = sizeof(retdata1); 172 | ret = acrd_tx_read(tx, "/tmp/2", retdata1, &retdata1_len, 0, 0); 173 | g_assert(ret == ACRD_SUCCESS); 174 | retdata2_len = sizeof(retdata2); 175 | ret = acrd_tx_read(tx, "/tmp/1", retdata2, &retdata2_len, 0, 0); 176 | g_assert(ret == ACRD_SUCCESS); 177 | ret = acrd_tx_commit(tx, 0); 178 | g_assert(ret == ACRD_ERR_NOTFOUND); 179 | acrd_tx_close(tx); 180 | 181 | tx = acrd_tx_init(h); 182 | g_assert(tx != NULL); 183 | retdata1_len = sizeof(retdata1); 184 | ret = acrd_tx_read(tx, "/tmp/0", retdata1, &retdata1_len, 0, 0); 185 | g_assert(ret == ACRD_SUCCESS); 186 | retdata2_len = sizeof(retdata2); 187 | ret = acrd_tx_read(tx, "/tmp/2", retdata2, &retdata2_len, 0, 0); 188 | g_assert(ret == ACRD_SUCCESS); 189 | ret = acrd_tx_commit(tx, 0); 190 | g_assert(ret == ACRD_ERR_NOTFOUND); 191 | acrd_tx_close(tx); 192 | } 193 | 194 | static void test_txn_update_if_exists(struct acrd_fixture *fixture, gconstpointer p) 195 | { 196 | struct acrd_handle *h = fixture->handle; 197 | const char data[] = "data"; 198 | const char newdata[] = "newdata"; 199 | char retdata[32]; 200 | uint32_t retdata_len; 201 | int ret; 202 | struct acrd_tx *tx; 203 | 204 | tx = acrd_tx_init(h); 205 | g_assert(tx != NULL); 206 | ret = acrd_tx_del(tx, "/tmp/0", 0); 207 | g_assert(ret == ACRD_SUCCESS); 208 | ret = acrd_tx_write(tx, "/tmp/0", newdata, sizeof(newdata), 0, ACRD_FLAG_CREATE); 209 | g_assert(ret == ACRD_SUCCESS); 210 | ret = acrd_tx_commit(tx, 0); 211 | g_assert(ret == ACRD_ERR_NOTFOUND); 212 | acrd_tx_close(tx); 213 | 214 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 215 | g_assert(ret == ACRD_SUCCESS); 216 | 217 | tx = acrd_tx_init(h); 218 | g_assert(tx != NULL); 219 | ret = acrd_tx_del(tx, "/tmp/0", 0); 220 | g_assert(ret == ACRD_SUCCESS); 221 | ret = acrd_tx_write(tx, "/tmp/0", newdata, sizeof(newdata), 0, ACRD_FLAG_CREATE); 222 | g_assert(ret == ACRD_SUCCESS); 223 | ret = acrd_tx_commit(tx, 0); 224 | g_assert(ret == ACRD_SUCCESS); 225 | acrd_tx_close(tx); 226 | 227 | retdata_len = sizeof(retdata); 228 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 229 | g_assert(ret == ACRD_SUCCESS); 230 | g_assert(retdata_len == sizeof(newdata)); 231 | g_assert_cmpstr(retdata, ==, newdata); 232 | } 233 | 234 | static void test_txn_increment(struct acrd_fixture *fixture, gconstpointer p) 235 | { 236 | struct acrd_handle *h = fixture->handle; 237 | const char data[] = "3"; 238 | const char newdata[] = "4"; 239 | char retdata[32]; 240 | uint32_t retdata_len; 241 | int ret; 242 | struct acrd_tx *tx; 243 | 244 | tx = acrd_tx_init(h); 245 | g_assert(tx != NULL); 246 | ret = acrd_tx_cmp(tx, "/tmp/0", data, sizeof(data), 0); 247 | g_assert(ret == ACRD_SUCCESS); 248 | ret = acrd_tx_write(tx, "/tmp/0", newdata, sizeof(newdata), 0, 0); 249 | g_assert(ret == ACRD_SUCCESS); 250 | ret = acrd_tx_commit(tx, 0); 251 | g_assert(ret == ACRD_ERR_NOTFOUND); 252 | acrd_tx_close(tx); 253 | 254 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 255 | g_assert(ret == ACRD_SUCCESS); 256 | 257 | tx = acrd_tx_init(h); 258 | g_assert(tx != NULL); 259 | ret = acrd_tx_cmp(tx, "/tmp/0", data, sizeof(data), 0); 260 | g_assert(ret == ACRD_SUCCESS); 261 | ret = acrd_tx_write(tx, "/tmp/0", newdata, sizeof(newdata), 0, 0); 262 | g_assert(ret == ACRD_SUCCESS); 263 | ret = acrd_tx_commit(tx, 0); 264 | g_assert(ret == ACRD_SUCCESS); 265 | acrd_tx_close(tx); 266 | 267 | retdata_len = sizeof(retdata); 268 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 269 | g_assert(ret == ACRD_SUCCESS); 270 | g_assert(retdata_len == sizeof(newdata)); 271 | g_assert_cmpstr(retdata, ==, newdata); 272 | 273 | tx = acrd_tx_init(h); 274 | g_assert(tx != NULL); 275 | ret = acrd_tx_cmp(tx, "/tmp/0", data, sizeof(data), 0); 276 | g_assert(ret == ACRD_SUCCESS); 277 | ret = acrd_tx_write(tx, "/tmp/0", newdata, sizeof(newdata), 0, 0); 278 | g_assert(ret == ACRD_SUCCESS); 279 | ret = acrd_tx_commit(tx, 0); 280 | g_assert(ret == ACRD_ERR_NOTEQUAL); 281 | acrd_tx_close(tx); 282 | } 283 | 284 | static void test_txn_ainc(struct acrd_fixture *fixture, gconstpointer p) 285 | { 286 | struct acrd_handle *h = fixture->handle; 287 | uint32_t data = 5555; 288 | uint32_t newdata = 5556; 289 | uint32_t *readdata; 290 | uint32_t delta = 1; 291 | char retdata[32]; 292 | uint32_t retdata_len = sizeof(data); 293 | int ret; 294 | struct acrd_tx *tx; 295 | 296 | tx = acrd_tx_init(h); 297 | g_assert(tx != NULL); 298 | ret = acrd_tx_atomic_inc(tx, "/tmp/0", &delta, sizeof(uint32_t), 0, 0); 299 | g_assert(ret == ACRD_SUCCESS); 300 | ret = acrd_tx_commit(tx, 0); 301 | g_assert(ret == ACRD_ERR_NOTFOUND); 302 | acrd_tx_close(tx); 303 | 304 | ret = acrd_write(h, "/tmp/0", &data, sizeof(uint32_t), 0, ACRD_FLAG_CREATE); 305 | g_assert(ret == ACRD_SUCCESS); 306 | 307 | tx = acrd_tx_init(h); 308 | g_assert(tx != NULL); 309 | ret = acrd_tx_atomic_inc(tx, "/tmp/0", &delta, sizeof(uint32_t), 0, 0); 310 | g_assert(ret == ACRD_SUCCESS); 311 | ret = acrd_tx_commit(tx, 0); 312 | g_assert(ret == ACRD_SUCCESS); 313 | acrd_tx_close(tx); 314 | 315 | ret = acrd_read(h, "/tmp/0", &retdata, &retdata_len, 0, ACRD_FLAG_CREATE); 316 | g_assert(ret == ACRD_SUCCESS); 317 | readdata = (uint32_t *)retdata; 318 | g_assert(*readdata == newdata); 319 | } 320 | 321 | static void test_txn_merge(struct acrd_fixture *fixture, gconstpointer p) 322 | { 323 | struct acrd_handle *h = fixture->handle; 324 | const char data1[] = "data1"; 325 | const char data2[] = "data2"; 326 | int ret; 327 | struct acrd_tx *tx; 328 | 329 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE); 330 | g_assert(ret == ACRD_SUCCESS); 331 | ret = acrd_write(h, "/tmp/1", data1, sizeof(data1), 0, ACRD_FLAG_CREATE); 332 | g_assert(ret == ACRD_SUCCESS); 333 | ret = acrd_write(h, "/tmp/2", data2, sizeof(data2), 0, ACRD_FLAG_CREATE); 334 | g_assert(ret == ACRD_SUCCESS); 335 | 336 | tx = acrd_tx_init(h); 337 | g_assert(tx != NULL); 338 | ret = acrd_tx_scmp(tx, "/tmp/0", "/tmp/1", 0); 339 | g_assert(ret == ACRD_SUCCESS); 340 | ret = acrd_tx_del(tx, "/tmp/1", 0); 341 | g_assert(ret == ACRD_SUCCESS); 342 | ret = acrd_tx_commit(tx, 0); 343 | g_assert(ret == ACRD_SUCCESS); 344 | acrd_tx_close(tx); 345 | 346 | ret = acrd_del(h, "/tmp/1", 0); 347 | g_assert(ret == ACRD_ERR_NOTFOUND); 348 | 349 | tx = acrd_tx_init(h); 350 | g_assert(tx != NULL); 351 | ret = acrd_tx_scmp(tx, "/tmp/0", "/tmp/1", 0); 352 | g_assert(ret == ACRD_SUCCESS); 353 | ret = acrd_tx_del(tx, "/tmp/1", 0); 354 | g_assert(ret == ACRD_SUCCESS); 355 | ret = acrd_tx_commit(tx, 0); 356 | g_assert(ret == ACRD_ERR_NOTFOUND); 357 | acrd_tx_close(tx); 358 | 359 | tx = acrd_tx_init(h); 360 | g_assert(tx != NULL); 361 | ret = acrd_tx_scmp(tx, "/tmp/0", "/tmp/2", 0); 362 | g_assert(ret == ACRD_SUCCESS); 363 | ret = acrd_tx_del(tx, "/tmp/2", 0); 364 | g_assert(ret == ACRD_SUCCESS); 365 | ret = acrd_tx_commit(tx, 0); 366 | g_assert(ret == ACRD_ERR_NOTEQUAL); 367 | acrd_tx_close(tx); 368 | } 369 | 370 | static void test_txn_swap(struct acrd_fixture *fixture, gconstpointer p) 371 | { 372 | struct acrd_handle *h = fixture->handle; 373 | const char data1[] = "data1"; 374 | const char data2[] = "data2"; 375 | int ret; 376 | struct acrd_tx *tx; 377 | char retdata[32]; 378 | uint32_t retdata_len; 379 | 380 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, 381 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 382 | g_assert(ret == ACRD_SUCCESS); 383 | ret = acrd_write(h, "/tmp/1", data2, sizeof(data2), 0, 384 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 385 | g_assert(ret == ACRD_SUCCESS); 386 | 387 | tx = acrd_tx_init(h); 388 | g_assert(tx != NULL); 389 | ret = acrd_tx_copy(tx, "/tmp/0", "/tmp/2", ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 390 | g_assert(ret == ACRD_SUCCESS); 391 | ret = acrd_tx_copy(tx, "/tmp/1", "/tmp/0", 0); 392 | g_assert(ret == ACRD_SUCCESS); 393 | ret = acrd_tx_copy(tx, "/tmp/2", "/tmp/1", 0); 394 | g_assert(ret == ACRD_SUCCESS); 395 | ret = acrd_tx_del(tx, "/tmp/2", 0); 396 | g_assert(ret == ACRD_SUCCESS); 397 | ret = acrd_tx_commit(tx, 0); 398 | g_assert(ret == ACRD_SUCCESS); 399 | acrd_tx_close(tx); 400 | 401 | retdata_len = sizeof(retdata); 402 | ret = acrd_read(h, "/tmp/0", retdata, &retdata_len, 0, 0); 403 | g_assert(ret == ACRD_SUCCESS); 404 | g_assert(retdata_len == sizeof(data2)); 405 | g_assert_cmpstr(retdata, ==, data2); 406 | retdata_len = sizeof(retdata); 407 | ret = acrd_read(h, "/tmp/1", retdata, &retdata_len, 0, 0); 408 | g_assert(ret == ACRD_SUCCESS); 409 | g_assert(retdata_len == sizeof(data1)); 410 | g_assert_cmpstr(retdata, ==, data1); 411 | 412 | ret = acrd_write(h, "/tmp/2", data1, sizeof(data1), 0, 413 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 414 | g_assert(ret == ACRD_SUCCESS); 415 | 416 | tx = acrd_tx_init(h); 417 | g_assert(tx != NULL); 418 | ret = acrd_tx_copy(tx, "/tmp/0", "/tmp/2", ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 419 | g_assert(ret == ACRD_SUCCESS); 420 | ret = acrd_tx_copy(tx, "/tmp/1", "/tmp/0", 0); 421 | g_assert(ret == ACRD_SUCCESS); 422 | ret = acrd_tx_copy(tx, "/tmp/2", "/tmp/1", 0); 423 | g_assert(ret == ACRD_SUCCESS); 424 | ret = acrd_tx_del(tx, "/tmp/2", 0); 425 | g_assert(ret == ACRD_SUCCESS); 426 | ret = acrd_tx_commit(tx, 0); 427 | g_assert(ret == ACRD_ERR_EXIST); 428 | acrd_tx_close(tx); 429 | } 430 | 431 | int main(int argc, char **argv) 432 | { 433 | g_test_init(&argc, &argv, NULL); 434 | g_test_add("/txn/multi_write", struct acrd_fixture, NULL, 435 | test_txn_setup, test_txn_multi_write, test_txn_teardown); 436 | g_test_add("/txn/multi_read", struct acrd_fixture, NULL, 437 | test_txn_setup, test_txn_multi_read, test_txn_teardown); 438 | g_test_add("/txn/update_if_exists", struct acrd_fixture, NULL, 439 | test_txn_setup, test_txn_update_if_exists, test_txn_teardown); 440 | g_test_add("/txn/increment", struct acrd_fixture, NULL, 441 | test_txn_setup, test_txn_increment, test_txn_teardown); 442 | g_test_add("/txn/merge", struct acrd_fixture, NULL, 443 | test_txn_setup, test_txn_merge, test_txn_teardown); 444 | g_test_add("/txn/swap", struct acrd_fixture, NULL, 445 | test_txn_setup, test_txn_swap, test_txn_teardown); 446 | g_test_add("/txn/ainc", struct acrd_fixture, NULL, 447 | test_txn_setup, test_txn_ainc, test_txn_teardown); 448 | 449 | return g_test_run(); 450 | } 451 | -------------------------------------------------------------------------------- /test/test-watch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | #include "util.h" 17 | #include "list.h" 18 | 19 | struct acrd_fixture { 20 | struct acrd_handle *handle; 21 | }; 22 | 23 | struct acrd_path_list_entry { 24 | char *path; 25 | 26 | struct list_head list; 27 | }; 28 | 29 | static void test_watch_list_cb(struct acrd_handle *h, const char *path, void *arg) 30 | { 31 | struct acrd_path_list_entry *entry = malloc(sizeof(*entry)); 32 | struct list_head *head = arg; 33 | 34 | entry->path = strdup(path); 35 | list_add_tail(&entry->list, head); 36 | } 37 | 38 | static void test_watch_setup(struct acrd_fixture *fixture, gconstpointer p) 39 | { 40 | struct acrd_handle *h; 41 | LIST_HEAD(path_list); 42 | struct acrd_listcb listcb = { 43 | .cb = test_watch_list_cb, 44 | .arg = &path_list, 45 | }; 46 | struct acrd_path_list_entry *entry, *n; 47 | 48 | h = acrd_init("localhost", 9090, NULL, NULL, NULL); 49 | g_assert(h != NULL); 50 | 51 | /* cleanup */ 52 | acrd_list(h, "/tmp/", 0, &listcb); 53 | list_for_each_entry_safe(entry, n, &path_list, list) { 54 | acrd_del(h, entry->path, 0); 55 | 56 | free(entry->path); 57 | list_del(&entry->list); 58 | free(entry); 59 | } 60 | 61 | fixture->handle = h; 62 | } 63 | 64 | static void test_watch_teardown(struct acrd_fixture *fixture, gconstpointer p) 65 | { 66 | acrd_close(fixture->handle); 67 | } 68 | 69 | static void test_watch_cb(struct acrd_handle *bh, struct acrd_watch_info *info, 70 | void *arg) 71 | { 72 | struct acrd_watch_info **retdata = arg; 73 | 74 | g_assert(*retdata == NULL); 75 | *retdata = info; 76 | } 77 | 78 | static void test_watch_add_and_rm(struct acrd_fixture *fixture, gconstpointer p) 79 | { 80 | struct acrd_handle *h = fixture->handle; 81 | struct acrd_watch_info *info1, *info2, *info3; 82 | int ret; 83 | 84 | info1 = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, NULL); 85 | g_assert(info1 != NULL); 86 | 87 | info2 = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, NULL); 88 | g_assert(info2 != NULL); 89 | 90 | info3 = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, NULL); 91 | g_assert(info3 != NULL); 92 | 93 | ret = acrd_rm_watch(h, info3); 94 | g_assert(ret == ACRD_SUCCESS); 95 | 96 | ret = acrd_rm_watch(h, info1); 97 | g_assert(ret == ACRD_SUCCESS); 98 | 99 | ret = acrd_rm_watch(h, info1); 100 | g_assert(ret == ACRD_ERR_NOTFOUND); 101 | 102 | ret = acrd_rm_watch(h, info2); 103 | g_assert(ret == ACRD_SUCCESS); 104 | 105 | free(info1); 106 | free(info2); 107 | free(info3); 108 | } 109 | 110 | static void test_watch_created(struct acrd_fixture *fixture, gconstpointer p) 111 | { 112 | struct acrd_handle *h = fixture->handle; 113 | struct acrd_watch_info *info, *retdata = NULL; 114 | const char data1[] = "data1"; 115 | const char data2[] = "data2"; 116 | int ret; 117 | 118 | info = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, &retdata); 119 | g_assert(info != NULL); 120 | g_assert(retdata == NULL); 121 | 122 | ret = acrd_write(h, "/tmp/0", data1, sizeof(data1), 0, ACRD_FLAG_CREATE); 123 | g_assert(ret == ACRD_SUCCESS); 124 | g_assert(retdata == info); 125 | g_assert(info->events == ACRD_EVENT_CREATED); 126 | 127 | retdata = NULL; 128 | ret = acrd_write(h, "/tmp/0", data2, sizeof(data2), 0, 129 | ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 130 | g_assert(ret == ACRD_ERR_EXIST); 131 | g_assert(retdata == NULL); 132 | 133 | ret = acrd_rm_watch(h, info); 134 | g_assert(ret == ACRD_SUCCESS); 135 | 136 | free(info); 137 | } 138 | 139 | static void test_watch_deleted(struct acrd_fixture *fixture, gconstpointer p) 140 | { 141 | struct acrd_handle *h = fixture->handle; 142 | struct acrd_watch_info *info, *retdata = NULL; 143 | const char data[] = "data"; 144 | int ret; 145 | 146 | info = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, &retdata); 147 | g_assert(info != NULL); 148 | g_assert(retdata == NULL); 149 | 150 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 151 | g_assert(ret == ACRD_SUCCESS); 152 | 153 | retdata = NULL; 154 | ret = acrd_del(h, "/tmp/0", 0); 155 | g_assert(ret == ACRD_SUCCESS); 156 | g_assert(retdata == info); 157 | g_assert(info->events == ACRD_EVENT_DELETED); 158 | 159 | retdata = NULL; 160 | ret = acrd_del(h, "/tmp/0", 0); 161 | g_assert(ret == ACRD_ERR_NOTFOUND); 162 | g_assert(retdata == NULL); 163 | 164 | ret = acrd_rm_watch(h, info); 165 | g_assert(ret == ACRD_SUCCESS); 166 | 167 | free(info); 168 | } 169 | 170 | static void test_watch_changed(struct acrd_fixture *fixture, gconstpointer p) 171 | { 172 | struct acrd_handle *h = fixture->handle; 173 | struct acrd_watch_info *info, *retdata = NULL; 174 | const char data[] = "data"; 175 | int ret; 176 | 177 | info = acrd_add_watch(h, "/tmp/0", ACRD_EVENT_ALL, test_watch_cb, &retdata); 178 | g_assert(info != NULL); 179 | g_assert(retdata == NULL); 180 | 181 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 182 | g_assert(ret == ACRD_SUCCESS); 183 | 184 | retdata = NULL; 185 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, 0); 186 | g_assert(ret == ACRD_SUCCESS); 187 | g_assert(retdata == info); 188 | g_assert(info->offset == 0); 189 | g_assert(info->events == ACRD_EVENT_CHANGED); 190 | 191 | retdata = NULL; 192 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 3, 0); 193 | g_assert(ret == ACRD_SUCCESS); 194 | g_assert(retdata == info); 195 | g_assert(info->offset == 3); 196 | g_assert(info->events == ACRD_EVENT_CHANGED); 197 | 198 | ret = acrd_rm_watch(h, info); 199 | g_assert(ret == ACRD_SUCCESS); 200 | 201 | free(info); 202 | } 203 | 204 | static void test_watch_copied(struct acrd_fixture *fixture, gconstpointer p) 205 | { 206 | struct acrd_handle *h = fixture->handle; 207 | struct acrd_watch_info *info, *retdata = NULL; 208 | const char data[] = "data"; 209 | int ret; 210 | 211 | info = acrd_add_watch(h, "/tmp/1", ACRD_EVENT_ALL, test_watch_cb, &retdata); 212 | g_assert(info != NULL); 213 | g_assert(retdata == NULL); 214 | 215 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 216 | g_assert(ret == ACRD_SUCCESS); 217 | 218 | retdata = NULL; 219 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE); 220 | g_assert(ret == ACRD_SUCCESS); 221 | g_assert(retdata == info); 222 | g_assert(info->events == ACRD_EVENT_COPIED); 223 | 224 | retdata = NULL; 225 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE); 226 | g_assert(ret == ACRD_SUCCESS); 227 | g_assert(retdata == info); 228 | g_assert(info->events == ACRD_EVENT_COPIED); 229 | 230 | retdata = NULL; 231 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", ACRD_FLAG_CREATE | ACRD_FLAG_EXCL); 232 | g_assert(ret == ACRD_ERR_EXIST); 233 | g_assert(retdata == NULL); 234 | 235 | retdata = NULL; 236 | ret = acrd_copy(h, "/tmp/0", "/tmp/1", 0); 237 | g_assert(ret == ACRD_SUCCESS); 238 | g_assert(retdata == info); 239 | g_assert(info->events == ACRD_EVENT_COPIED); 240 | 241 | ret = acrd_rm_watch(h, info); 242 | g_assert(ret == ACRD_SUCCESS); 243 | 244 | free(info); 245 | } 246 | 247 | static void __test_watch_mask(struct acrd_handle *h, uint32_t mask) 248 | { 249 | struct acrd_watch_info *info, *retdata = NULL; 250 | const char data[] = "data"; 251 | int ret; 252 | 253 | info = acrd_add_watch(h, "/tmp/0", mask, test_watch_cb, &retdata); 254 | g_assert(info != NULL); 255 | g_assert(retdata == NULL); 256 | 257 | ret = acrd_write(h, "/tmp/1", data, sizeof(data), 0, ACRD_FLAG_CREATE); 258 | g_assert(ret == ACRD_SUCCESS); 259 | 260 | retdata = NULL; 261 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, ACRD_FLAG_CREATE); 262 | g_assert(ret == ACRD_SUCCESS); 263 | if (mask & ACRD_EVENT_CREATED) { 264 | g_assert(retdata == info); 265 | g_assert(info->events == ACRD_EVENT_CREATED); 266 | } else 267 | g_assert(retdata == NULL); 268 | 269 | retdata = NULL; 270 | ret = acrd_write(h, "/tmp/0", data, sizeof(data), 0, 0); 271 | g_assert(ret == ACRD_SUCCESS); 272 | if (mask & ACRD_EVENT_CHANGED) { 273 | g_assert(retdata == info); 274 | g_assert(info->events == ACRD_EVENT_CHANGED); 275 | } else 276 | g_assert(retdata == NULL); 277 | 278 | retdata = NULL; 279 | ret = acrd_del(h, "/tmp/0", 0); 280 | g_assert(ret == ACRD_SUCCESS); 281 | if (mask & ACRD_EVENT_DELETED) { 282 | g_assert(retdata == info); 283 | g_assert(info->events == ACRD_EVENT_DELETED); 284 | } else 285 | g_assert(retdata == NULL); 286 | 287 | retdata = NULL; 288 | ret = acrd_copy(h, "/tmp/1", "/tmp/0", ACRD_FLAG_CREATE); 289 | g_assert(ret == ACRD_SUCCESS); 290 | if (mask & ACRD_EVENT_COPIED) { 291 | g_assert(retdata == info); 292 | g_assert(info->events == ACRD_EVENT_COPIED); 293 | } else 294 | g_assert(retdata == NULL); 295 | 296 | retdata = NULL; 297 | ret = acrd_del(h, "/tmp/0", 0); 298 | g_assert(ret == ACRD_SUCCESS); 299 | ret = acrd_del(h, "/tmp/1", 0); 300 | 301 | ret = acrd_rm_watch(h, info); 302 | g_assert(ret == ACRD_SUCCESS); 303 | 304 | free(info); 305 | } 306 | 307 | static void test_watch_mask(struct acrd_fixture *fixture, gconstpointer p) 308 | { 309 | struct acrd_handle *h = fixture->handle; 310 | 311 | __test_watch_mask(h, ACRD_EVENT_CREATED); 312 | __test_watch_mask(h, ACRD_EVENT_CHANGED); 313 | __test_watch_mask(h, ACRD_EVENT_DELETED); 314 | __test_watch_mask(h, ACRD_EVENT_COPIED); 315 | 316 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_CHANGED); 317 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_DELETED); 318 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_COPIED); 319 | __test_watch_mask(h, ACRD_EVENT_CHANGED | ACRD_EVENT_DELETED); 320 | __test_watch_mask(h, ACRD_EVENT_CHANGED | ACRD_EVENT_COPIED); 321 | __test_watch_mask(h, ACRD_EVENT_DELETED | ACRD_EVENT_COPIED); 322 | 323 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_CHANGED | ACRD_EVENT_DELETED); 324 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_CHANGED | ACRD_EVENT_COPIED); 325 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_DELETED | ACRD_EVENT_COPIED); 326 | __test_watch_mask(h, ACRD_EVENT_CHANGED | ACRD_EVENT_DELETED | ACRD_EVENT_COPIED); 327 | 328 | __test_watch_mask(h, ACRD_EVENT_CREATED | ACRD_EVENT_CHANGED | ACRD_EVENT_DELETED | ACRD_EVENT_COPIED); 329 | __test_watch_mask(h, ACRD_EVENT_ALL); 330 | } 331 | 332 | int main(int argc, char **argv) 333 | { 334 | g_test_init(&argc, &argv, NULL); 335 | g_test_add("/watch/add_and_rm", struct acrd_fixture, NULL, 336 | test_watch_setup, test_watch_add_and_rm, test_watch_teardown); 337 | g_test_add("/watch/created", struct acrd_fixture, NULL, 338 | test_watch_setup, test_watch_created, test_watch_teardown); 339 | g_test_add("/watch/deleted", struct acrd_fixture, NULL, 340 | test_watch_setup, test_watch_deleted, test_watch_teardown); 341 | g_test_add("/watch/changed", struct acrd_fixture, NULL, 342 | test_watch_setup, test_watch_changed, test_watch_teardown); 343 | g_test_add("/watch/copied", struct acrd_fixture, NULL, 344 | test_watch_setup, test_watch_copied, test_watch_teardown); 345 | g_test_add("/watch/mask", struct acrd_fixture, NULL, 346 | test_watch_setup, test_watch_mask, test_watch_teardown); 347 | 348 | return g_test_run(); 349 | } 350 | -------------------------------------------------------------------------------- /test/watch_files.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | 17 | static void watch_cb(struct acrd_handle *bh, struct acrd_watch_info *info, void *arg) 18 | { 19 | printf("%s\n", info->path); 20 | } 21 | 22 | /* test code */ 23 | int main(int argc, char *argv[]) 24 | { 25 | struct acrd_handle *bh; 26 | uint32_t mask = ACRD_EVENT_ALL | ACRD_EVENT_PREFIX; 27 | char *hostname; 28 | struct acrd_watch_info *info; 29 | 30 | if (argc < 2) { 31 | printf("usage: watch_nodes [hostname]\n"); 32 | return 1; 33 | } 34 | 35 | hostname = argv[1]; 36 | bh = acrd_init(hostname, 9090, NULL, NULL, NULL); 37 | 38 | info = acrd_add_watch(bh, "", mask, watch_cb, NULL); 39 | 40 | while (1) 41 | sleep(1); 42 | 43 | acrd_close(bh); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /test/watch_nodes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License version 6 | * 2 as published by the Free Software Foundation. 7 | * 8 | * You should have received a copy of the GNU General Public License 9 | * along with this program. If not, see . 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #include "accord.h" 16 | 17 | static void join_cb(struct acrd_handle *bh, const uint64_t *memger_list, 18 | size_t member_list_entries, uint64_t nodeid, void *arg) 19 | { 20 | printf("node joined: %lx\n", nodeid); 21 | } 22 | 23 | static void leave_cb(struct acrd_handle *bh, const uint64_t *memger_list, 24 | size_t member_list_entries, uint64_t nodeid, void *arg) 25 | { 26 | printf("node left: %lx\n", nodeid); 27 | } 28 | 29 | /* test code */ 30 | int main(int argc, char *argv[]) 31 | { 32 | struct acrd_handle *bh; 33 | char *hostname; 34 | 35 | if (argc < 2) { 36 | printf("usage: watch_nodes [hostname]\n"); 37 | return 1; 38 | } 39 | 40 | hostname = argv[1]; 41 | bh = acrd_init(hostname, 9090, join_cb, leave_cb, NULL); 42 | 43 | while (1) 44 | sleep(1); 45 | 46 | acrd_close(bh); 47 | 48 | return 0; 49 | } 50 | --------------------------------------------------------------------------------