├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── async.h ├── examples ├── Makefile ├── fib.c └── spawn.c ├── package.json └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | tmp 20 | async-test 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Joseph Werle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -L/usr/local/lib -luv -pthread 2 | 3 | all: clean test 4 | 5 | clean: 6 | rm -f async-test 7 | 8 | test: 9 | $(CC) test.c $(CFLAGS) -o async-test 10 | LD_LIBRARY_PATH=/usr/local/lib ./async-test 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | uv-async 2 | ======= 3 | 4 | Asynchronous goodies built on libuv 5 | 6 | # install 7 | 8 | ```sh 9 | $ clib install jwerle/uv-async 10 | ``` 11 | 12 | # usage 13 | 14 | Starting async work can be done with the `async(env, loop)` macro 15 | function. 16 | 17 | ```c 18 | 19 | int 20 | main (void) { 21 | uv_loop_t *loop = uv_default_loop(); 22 | 23 | async(env, loop) { 24 | queue(env, job); 25 | wait(env, 500, called_500ms_later); 26 | interval(env, 300, every_300ms); 27 | } 28 | 29 | return uv_run(loop, UV_RUN_DEFAULT); 30 | } 31 | ``` 32 | 33 | ## queue 34 | 35 | You can queue a task with the `queue(env, fn)` macro function 36 | 37 | ```c 38 | async(env, loop) { 39 | queue(env, job_fn); 40 | } 41 | ``` 42 | 43 | ## timeout 44 | 45 | You can set a task after a timeout with the `wait(env, ms, fn)` macro 46 | function. 47 | 48 | ```c 49 | async(env, loop) { 50 | wait(env, 500, job_fn); 51 | } 52 | ``` 53 | 54 | ## interval 55 | 56 | You can set an interval task with the `interval(env, ms, fn)` macro 57 | function. 58 | 59 | ```c 60 | async(env, loop) { 61 | interval(env, 500, job_fn); 62 | } 63 | ``` 64 | 65 | You can stop the inteval by setting the `rc` to `1` on the 66 | `async_work_data_t *data` pointer passed to the function. 67 | 68 | ```c 69 | static void 70 | on_interval (async_work_data_t *work) { 71 | if (++interval_count > 10) { 72 | printf("interval limit reached. Stopping.. \n"); 73 | data->rc = 1; 74 | } else { 75 | printf("interval #%d\n", interval_count); 76 | } 77 | } 78 | ``` 79 | 80 | ## spawn 81 | 82 | You can spawn a system command with the `spawn(env, cmd, fn)` macro. The 83 | `fn` will be called when the command has returned. The return code and 84 | signal code are attached to the `async_work_data_t *` pointer provided 85 | to the callback `fn`. 86 | 87 | ```c 88 | #include "async.h" 89 | #include 90 | #include 91 | #include 92 | #include 93 | 94 | staic void 95 | on_spawn (async_work_data_t *work) { 96 | assert(0 == work->rc); 97 | assert(0 == work->signal); 98 | assert(work->process); 99 | 100 | struct stat s; 101 | assert(0 == stat("./foo", &s)); 102 | assert(0 == stat("./foo/bar", &s)); 103 | assert(0 == stat("./foo/bar/biz", &s)); 104 | } 105 | 106 | int 107 | main (void) { 108 | async(env, uv_default_loop()) { 109 | char *cmd[] = { "mkdir", "-p", "./foo/bar/biz" }; 110 | spawn(env, cmd, on_spawn); 111 | } 112 | 113 | return uv_run(uv_default_loop(), UV_RUN_DEFAULT); 114 | } 115 | ``` 116 | 117 | # license 118 | 119 | MIT 120 | -------------------------------------------------------------------------------- /async.h: -------------------------------------------------------------------------------- 1 | #ifndef ASYNC_H 2 | #define ASYNC_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define async(env, l) \ 9 | async_env_t *env = malloc(sizeof(async_env_t)); \ 10 | env->loop = l; \ 11 | env->handle = NULL; \ 12 | env->stdio_count = 0; \ 13 | 14 | #define async_work_init(e, fn, d, f) { \ 15 | async_work_data_t *data = \ 16 | malloc(sizeof(async_work_data_t)); \ 17 | uv_work_t *work = malloc(sizeof(uv_work_t)); \ 18 | data->env = e; \ 19 | data->cb = fn; \ 20 | data->data = (void *)d; \ 21 | data->flags = 0; \ 22 | data->flags |= f; \ 23 | work->data = (void *) data; \ 24 | e->handle = (uv_async_t *) malloc(sizeof(uv_async_t)); \ 25 | uv_async_init(e->loop, e->handle, \ 26 | _handle_async_callback); \ 27 | uv_queue_work(e->loop, work, \ 28 | _handle_async, \ 29 | _handle_after_async); \ 30 | } 31 | 32 | #define queue(env, fn) { \ 33 | async_work_init(env, fn, 1, ASYNC_DEFER); \ 34 | } 35 | 36 | #define wait(env, ms, fn) { \ 37 | async_work_init(env, fn, ms, ASYNC_DEFER); \ 38 | } 39 | 40 | #define interval(env, ms, fn) { \ 41 | async_work_init(env, fn, ms, ASYNC_INTERVAL); \ 42 | } 43 | 44 | #define spawn(env, cmd, fn) { \ 45 | _spawn_async(env, sizeof(cmd) / sizeof(cmd[0]), cmd, fn);\ 46 | } 47 | 48 | #define MAX_ASYNC_STDIO 8 49 | 50 | // FLAGS 51 | 52 | #define ASYNC_DEFER 0x1 53 | #define ASYNC_INTERVAL 0x2 54 | 55 | struct async_work_data; 56 | 57 | typedef struct async_env { 58 | uv_async_t *handle; 59 | uv_loop_t *loop; 60 | uv_stdio_container_t stdio[MAX_ASYNC_STDIO]; 61 | void *data; 62 | int stdio_count; 63 | int flags; 64 | } async_env_t; 65 | 66 | typedef struct async_work_data { 67 | async_env_t *env; 68 | uv_process_t *process; 69 | void (*cb)(struct async_work_data *data); 70 | void *data; 71 | void *extra; 72 | int rc; 73 | int signal; 74 | int flags; 75 | int err; 76 | } async_work_data_t; 77 | 78 | typedef void (async_cb)(async_work_data_t *work); 79 | 80 | static void 81 | _handle_async_callback (uv_async_t *handle, int rc); 82 | 83 | static void 84 | _handle_async (uv_work_t *work); 85 | 86 | static void 87 | _handle_after_async (uv_work_t *work, int rc); 88 | 89 | static void 90 | _spawn_async (async_env_t *env, int argc, char *args[], void (*fn)(async_work_data_t *work)); 91 | 92 | static void 93 | _handle_spawn_async (uv_process_t *req, int64_t rc, int sig); 94 | 95 | 96 | static void 97 | _handle_async (uv_work_t *work) { 98 | async_work_data_t *data = (async_work_data_t *)work->data; 99 | async_env_t *env = (async_env_t *)data->env; 100 | int us = 0; 101 | 102 | if (ASYNC_DEFER == (ASYNC_DEFER & data->flags) || 103 | ASYNC_INTERVAL == (ASYNC_INTERVAL & data->flags)) { 104 | us = ((long) data->data) * 1000; 105 | } 106 | 107 | if (ASYNC_INTERVAL == (ASYNC_INTERVAL & data->flags)) { 108 | while (1 != data->rc) { 109 | usleep(us); 110 | data->cb(data); 111 | } 112 | return; 113 | } 114 | 115 | usleep(us); 116 | data->cb(data); 117 | } 118 | 119 | static void 120 | _handle_after_async (uv_work_t *work, int rc) { 121 | async_work_data_t *data = (async_work_data_t *)work->data; 122 | async_env_t *env = (async_env_t *)data->env; 123 | uv_async_t *handle = env->handle; 124 | handle->data = data; 125 | 126 | uv_async_send(handle); 127 | } 128 | 129 | static void 130 | _handle_async_callback (uv_async_t *handle, int rc) { 131 | uv_work_t *work = (uv_work_t *) handle->data; 132 | async_work_data_t *data = (async_work_data_t *)work->data; 133 | async_env_t *env = (async_env_t *)data->env; 134 | 135 | if (0 == uv_is_closing((uv_handle_t *)handle)) { 136 | uv_close((uv_handle_t *)handle, NULL); 137 | } 138 | 139 | free(work); 140 | } 141 | 142 | static void 143 | _spawn_async (async_env_t *env, int argc, char *args[], void (*fn)(async_work_data_t *work)) { 144 | int i = 0; 145 | int n = 0; 146 | uv_process_options_t *opts = malloc(sizeof(uv_process_options_t)); 147 | uv_process_t *process = malloc(sizeof(uv_process_t)); 148 | async_work_data_t *data = malloc(sizeof(async_work_data_t)); 149 | 150 | args[argc] = NULL; 151 | opts->exit_cb = _handle_spawn_async; 152 | opts->args = args; 153 | opts->file = args[0]; 154 | opts->flags = 0; 155 | 156 | data->extra = (void *) opts; 157 | 158 | if (NULL != env->stdio) { 159 | opts->stdio = env->stdio; 160 | 161 | for (; i < MAX_ASYNC_STDIO; ++i) { 162 | if (NULL != env->stdio[i].data.stream) n++; 163 | } 164 | 165 | opts->stdio_count = n; 166 | } 167 | 168 | data->env = env; 169 | data->cb = fn; 170 | process->data = (void *) data; 171 | 172 | if (0 != uv_spawn(env->loop, process, opts)) { 173 | data->err = 1; 174 | fn(data); 175 | free(data); 176 | free(process); 177 | } 178 | } 179 | 180 | 181 | static void 182 | _handle_spawn_async (uv_process_t *process, int64_t rc, int sig) { 183 | async_work_data_t *work = (async_work_data_t *) process->data; 184 | async_cb *cb = (async_cb *) work->cb; 185 | work->process = process; 186 | work->signal = sig; 187 | work->rc = rc; 188 | if (NULL != work->cb) { 189 | cb(work); 190 | } 191 | 192 | uv_close((uv_handle_t *) process, NULL); 193 | free(work); 194 | } 195 | 196 | #endif 197 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS = -std=c99 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -luv 3 | 4 | all: clean test 5 | 6 | clean: 7 | rm -f async-test 8 | 9 | test: 10 | $(CC) test.c -$(CFLAGS) -o async-test 11 | ./async-test 12 | -------------------------------------------------------------------------------- /examples/fib.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypercore-cxx/uv-async/d8e9cf8cc33bcded90ba1d83ef5466338ba9bb90/examples/fib.c -------------------------------------------------------------------------------- /examples/spawn.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypercore-cxx/uv-async/d8e9cf8cc33bcded90ba1d83ef5466338ba9bb90/examples/spawn.c -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uv-async", 3 | "version": "0.0.1", 4 | "repo": "jwerle/uv-async", 5 | "description": "Async goodies for libuv", 6 | "keywords": ["async", "libuv", "util"], 7 | "license": "MIT", 8 | "src": ["async.h"] 9 | } 10 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "async.h" 5 | 6 | #define ENV_A 0x1 7 | #define ENV_B 0x2 8 | #define ENV_C 0x3 9 | 10 | static uv_loop_t *loop; 11 | 12 | static int jobs[4] = { 0, 0, 0, 0, }; 13 | static async_env_t *envs[4] = { }; 14 | 15 | static int loop_count = 0; 16 | static int env_count = 0; 17 | static int interval_count = 0; 18 | 19 | static void 20 | detail_job (char *name, async_work_data_t *data); 21 | 22 | static void 23 | job1 (async_work_data_t *data); 24 | 25 | static void 26 | job2 (async_work_data_t *data); 27 | 28 | static void 29 | job3 (async_work_data_t *data); 30 | 31 | static void 32 | job4 (async_work_data_t *data); 33 | 34 | static void 35 | spawn_job (async_work_data_t *data); 36 | 37 | static void 38 | pass_test (async_work_data_t *data); 39 | 40 | static void 41 | on_interval (async_work_data_t *data); 42 | 43 | int 44 | main (void) { 45 | loop = uv_default_loop(); 46 | assert(loop); 47 | 48 | async(env_a, loop) { 49 | env_a->flags |= ENV_A; 50 | 51 | assert(env_a); 52 | assert(loop); 53 | assert(NULL == env_a->handle); 54 | assert(env_a->loop); 55 | assert(env_a->flags); 56 | 57 | envs[env_count++] = env_a; 58 | 59 | printf("queue job1 in env (a)\n"); 60 | queue(env_a, job1); 61 | 62 | printf("wait 50ms for job2 in env (a)\n"); 63 | wait(env_a, 50, job2); 64 | 65 | printf("wait 20ms for job3 in env (a)\n"); 66 | wait(env_a, 20, job3); 67 | 68 | printf("queue job4 in env (a)\n"); 69 | queue(env_a, job4); 70 | } 71 | 72 | async(env_b, loop) { 73 | env_b->flags |= ENV_B; 74 | 75 | assert(env_b); 76 | assert(loop); 77 | assert(NULL == env_b->handle); 78 | assert(env_b->loop); 79 | assert(env_b->flags); 80 | 81 | envs[env_count++] = env_b; 82 | 83 | printf("queue job2 in env (b)\n"); 84 | queue(env_b, job2); 85 | 86 | printf("queue job1 in env (b)\n"); 87 | queue(env_b, job1); 88 | 89 | printf("starting interval in env (b)\n"); 90 | interval(env_b, 300, on_interval); 91 | } 92 | 93 | async(env_c, loop) { 94 | env_c->flags |= ENV_C; 95 | uv_pipe_t in, out; 96 | 97 | assert(env_c); 98 | assert(loop); 99 | assert(NULL == env_c->handle); 100 | assert(env_c->loop); 101 | assert(env_c->flags); 102 | 103 | envs[env_count++] = env_c; 104 | 105 | char *cmd[] = { "mkdir", "-p", "./tmp/test/dir" }; 106 | spawn(env_c, cmd, spawn_job); 107 | } 108 | 109 | async(env_d, loop) { 110 | assert(env_d); 111 | assert(loop); 112 | assert(NULL == env_d->handle); 113 | assert(env_d->loop); 114 | 115 | envs[env_count++] = env_d; 116 | 117 | long int ms = 1000; 118 | 119 | printf("waiting %d ms... to check state\n", (int) ms); 120 | wait(env_d, ms, pass_test); 121 | } 122 | 123 | uv_run(loop, UV_RUN_DEFAULT); 124 | 125 | return 0; 126 | } 127 | 128 | static void 129 | detail_job (char *name, async_work_data_t *data) { 130 | printf("- '%s' called", name); 131 | 132 | if (ENV_A == (ENV_A & data->env->flags)) { 133 | printf(" in env (a)"); 134 | } else if (ENV_B == (ENV_B & data->env->flags)) { 135 | printf(" in env (b)"); 136 | } else if (ENV_C == (ENV_C & data->env->flags)) { 137 | printf(" in env (c)"); 138 | } 139 | 140 | printf("\n"); 141 | } 142 | 143 | static void 144 | job1 (async_work_data_t *data) { 145 | jobs[0] = 1; 146 | detail_job("job1", data); 147 | } 148 | 149 | static void 150 | job2 (async_work_data_t *data) { 151 | jobs[1] = 1; 152 | detail_job("job2", data); 153 | } 154 | 155 | static void 156 | job3 (async_work_data_t *data) { 157 | jobs[2] = 1; 158 | detail_job("job3", data); 159 | } 160 | 161 | static void 162 | job4 (async_work_data_t *data) { 163 | jobs[3] = 1; 164 | detail_job("job4", data); 165 | } 166 | 167 | static void 168 | spawn_job (async_work_data_t *data) { 169 | uv_process_options_t *opts = (uv_process_options_t *)data->extra; 170 | struct stat s; 171 | 172 | detail_job("spawn", data); 173 | printf("- (spawn) rc = '%d'\n", data->rc); 174 | printf("- (spawn) signal = '%d'\n", data->signal); 175 | 176 | assert(0 == data->rc); 177 | assert(0 == data->signal); 178 | assert(data->process); 179 | 180 | if (0 == strcmp("mkdir", opts->file)) { 181 | assert(0 == stat("./tmp", &s)); 182 | assert(0 == stat("./tmp/test", &s)); 183 | assert(0 == stat("./tmp/test/dir", &s)); 184 | 185 | async(env, loop) { 186 | char *cmd[] = { "rm", "-rf", "./tmp" }; 187 | spawn(env, cmd, spawn_job); 188 | } 189 | } else if (0 == strcmp("rm", opts->file)) { 190 | assert(-1 == stat("./tmp", &s)); 191 | } 192 | } 193 | 194 | static void 195 | pass_test (async_work_data_t *data) { 196 | int i = 0; 197 | return; 198 | printf("testing state...\n"); 199 | assert(1 == jobs[0]); 200 | assert(1 == jobs[1]); 201 | assert(1 == jobs[2]); 202 | assert(1 == jobs[3]); 203 | } 204 | 205 | static void 206 | on_interval (async_work_data_t *data) { 207 | if (++interval_count > 10) { 208 | printf("interval limit reached. Stopping.. \n"); 209 | data->rc = 1; 210 | uv_stop((uv_loop_t *) data->env->loop); 211 | } else { 212 | printf("interval #%d\n", interval_count); 213 | } 214 | } 215 | --------------------------------------------------------------------------------