├── .gitignore ├── LICENSE.MIT ├── Makefile ├── README.md ├── bin └── .gitignore ├── include ├── array-heap.h ├── evn-bool.h ├── evn-buffer-list.h ├── evn-inbuf.h └── evn.h ├── ipc ├── Makefile ├── my-data.h ├── unix-socket-ipc-daemon.c └── unix-socket-ipc-remote-control.c ├── obj └── .gitignore ├── paired-threaded-ipc ├── Makefile ├── dummy-daemon.c ├── dummy-rc.c ├── dummy-rc.js ├── dummy-settings.c ├── dummy-worker-test.c ├── dummy-worker-thread.c ├── dummy-worker.c ├── include │ ├── dummy-settings.h │ ├── dummy-worker-thread.h │ ├── dummy-worker.h │ ├── dummy_settings_argp_option.c │ ├── dummy_settings_parse_opt.c │ ├── ev.h │ ├── loop.h │ └── rand.h ├── rand-test.c └── rand.c ├── reference-implementations-js ├── tcp.js ├── udp.js └── unix-socket.js └── src ├── array-heap.c ├── array-test.c ├── udp-echo-server.c ├── unix-echo-client.c └── unix-echo-server.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | *.dSYM 4 | -------------------------------------------------------------------------------- /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by AJ ONeal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC_OPTS = -Wall -Werror -lev -ggdb3 -I./include 2 | 3 | all: obj/array-heap.o bin/unix-echo-server bin/unix-echo-client bin/udp-echo-server 4 | 5 | clean: 6 | rm -f *.o bin/* 7 | 8 | bin/array-test: src/array-test.c obj/array-heap.o 9 | $(CC) $(CC_OPTS) -o $@ $< obj/array-heap.o 10 | 11 | obj/array-heap.o: src/array-heap.c include/array-heap.h 12 | $(CC) $(CC_OPTS) -o $@ -c $< 13 | 14 | bin/udp-echo-server: src/udp-echo-server.c 15 | $(CC) $(CC_OPTS) -o $@ $< 16 | 17 | bin/unix-echo-server: src/unix-echo-server.c obj/array-heap.o 18 | $(CC) $(CC_OPTS) -o $@ $< obj/array-heap.o 19 | 20 | bin/unix-echo-client: src/unix-echo-client.c 21 | $(CC) $(CC_OPTS) -o $@ $< 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moved 2 | ### [libev-examples](https://git.daplie.com/coolaj86/libev-examples) is now at [git.daplie.com/coolaj86/libev-examples](https://git.daplie.com/coolaj86/libev-examples) 3 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolaj86/libev-examples/48632c7e714d4b61122a05fab30906ea90f2d51b/bin/.gitignore -------------------------------------------------------------------------------- /include/array-heap.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | void* data; 3 | int index; 4 | int length; 5 | } array; 6 | 7 | int array_init(array* arr, int size); 8 | int array_push(array* arr, void* data); 9 | int array_grow(array* arr, int size); 10 | void array_free(array* arr, void (*free_element)(void*)); 11 | -------------------------------------------------------------------------------- /include/evn-bool.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Include this file to assert that you choose 3 | * 4 | * The Good Parts (TM) 5 | * 6 | * of C... even though it's C. 7 | * 8 | * Congratulations! Way to not contribute to the programming horrors of our day! 9 | */ 10 | #ifndef _BOOL_H_ 11 | #define _BOOL_H_ 12 | 13 | // You can choose to reserve the words true and false! 14 | #ifndef __cplusplus 15 | #ifndef _BOOL_ 16 | #define _BOOL_ 17 | typedef enum { false = 0, true = 1 } bool; 18 | #endif 19 | #endif 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/evn-buffer-list.h: -------------------------------------------------------------------------------- 1 | #ifndef EVN_BUFFER_LIST_H_ 2 | #define EVN_BUFFER_LIST_H_ 3 | 4 | #include // printf 5 | 6 | #define evn_error(...) printf("[EVN] " __VA_ARGS__) 7 | 8 | typedef struct { 9 | void* data; 10 | void* position; 11 | 12 | int free; 13 | int used; // -> index 14 | int capacity; // -> size 15 | } evn_buffer; 16 | 17 | typedef struct { 18 | evn_buffer* list; 19 | evn_buffer* current; 20 | 21 | int block_size; 22 | int index; 23 | int length; 24 | int used; 25 | int size; 26 | } evn_bufferlist; 27 | 28 | 29 | // Create a buffer of `size` bytes 30 | evn_buffer* evn_buffer_create(int size); 31 | // Create a buffer of `size` by `memcpy`ing `data` 32 | evn_buffer* evn_buffer_create_copy(void* data, int size); 33 | // Create a buffer from existing data where `index` is the first unused byte (or `size` if used) 34 | evn_buffer* evn_buffer_create_as(void* data, int size, int index); 35 | 36 | // Copy `size` bytes of `data` to existing buffer 37 | int evn_buffer_add(evn_buffer* buffer, void* data, int size); 38 | 39 | // Free all memory associated with the buffer 40 | void evn_buffer_destroy(evn_buffer* buffer); 41 | 42 | // Create a "smart" buffer 43 | evn_bufferlist* evn_bufferlist_create(int min_block_size, int slices); 44 | // Copy data into the buffer 45 | int evn_bufferlist_add(evn_bufferlist* bufferlist, void* data, int size); 46 | // Copy all data into a single buffer 47 | evn_buffer* evn_bufferlist_concat(evn_bufferlist* bufferlist); 48 | // Realloc the current buffer and then add a buffer after it 49 | //int evn_bufferlist_add_buffer(evn_bufferlist* bufferlist, evn_buffer* buffer); 50 | 51 | // Free all memory associated with the bufferlist and the buffers it contains 52 | void evn_bufferlist_destroy(evn_bufferlist* bufferlist); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/evn-inbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVN_INBUF_H_ 2 | #define _EVN_INBUF_H_ 3 | 4 | // This buffer is optimized for situations where the 5 | // drain is roughly equal to the increase. 6 | // (i.e. the user listens to the `drain` event before 7 | // stacking on another full chunk of data 8 | 9 | 10 | struct evn_inbuf; 11 | 12 | typedef struct { 13 | void* start; 14 | void* end; 15 | void* bottom; 16 | void* top; 17 | int size; 18 | } evn_inbuf; 19 | 20 | /* 21 | 22 | Anotomy of evn_inbuf 23 | 24 | start| |size| |end 25 | | | | | 26 | DATA |oooooooo|xxxx|oooooooo| 27 | | | 28 | bottom| |top 29 | | | 30 | |leading| |trailing| 31 | | | 32 | |used| 33 | 34 | The buffer will hold up to two chunks. 35 | It is assumed that one chunk should dain at about the same rate that the next chunk comes in. 36 | The buffer will realloc to become twice its size if the incoming data if space is no avaibale. 37 | It will memcpy if there is at least a chunks-worth of space already drained 38 | 39 | */ 40 | 41 | // Create a buffer about the size you expect the maximum incoming chunk to be 42 | // No need to get crazy, the size will grow on it's own, but it will never shrink 43 | evn_inbuf* evn_inbuf_create(int size); 44 | int evn_inbuf_init(evn_inbuf* buf, int size); 45 | // Add a chunk to the buffer 46 | int evn_inbuf_add(evn_inbuf* buf, void* data, int size); 47 | // Peek at a copy of some amount of data (you malloc the data) 48 | int evn_inbuf_peek(evn_inbuf* buf, void* data, int size); 49 | // Toss some amount of data out 50 | void evn_inbuf_toss(evn_inbuf* buf, int size); 51 | // Free memory associated with this inbuf 52 | void evn_inbuf_destroy(evn_inbuf* buf); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/evn.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVN_H_ 2 | #define _EVN_H_ 3 | 4 | #include // struct timeval 5 | 6 | // Network Stuff 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "evn-buffer-list.h" 18 | #include "evn-inbuf.h" 19 | #include "evn-bool.h" 20 | 21 | #ifndef EVN_DEBUG 22 | #if defined DEBUG 23 | #define EVN_DEBUG 1 24 | #else 25 | #define EVN_DEBUG 0 26 | #endif 27 | #endif 28 | 29 | #if EVN_DEBUG 30 | #include 31 | #define evn_debug(...) printf("[EVN] " __VA_ARGS__) 32 | #define evn_debugs(...) puts("[EVN] " __VA_ARGS__) 33 | #else 34 | #define evn_debug(...) 35 | #define evn_debugs(...) 36 | #endif 37 | 38 | 39 | struct evn_stream; 40 | struct evn_server; 41 | struct evn_exception; 42 | 43 | #define EVN_SERVER_P struct evn_server* server 44 | #define EVN_STREAM_P struct evn_stream* stream 45 | 46 | // Server Callbacks 47 | typedef void (evn_server_on_listen)(EV_P_ struct evn_server* server); 48 | typedef void (evn_server_on_connection)(EV_P_ struct evn_server* server, struct evn_stream* stream); 49 | typedef void (evn_server_on_close)(EV_P_ struct evn_server* server); 50 | 51 | // Client Callbacks 52 | typedef void (evn_stream_on_connect)(EV_P_ struct evn_stream* stream); 53 | typedef void (evn_stream_on_secure)(EV_P_ struct evn_stream* stream); // TODO Implement 54 | typedef void (evn_stream_on_data)(EV_P_ struct evn_stream* stream, void* blob, int size); 55 | typedef void (evn_stream_on_end)(EV_P_ struct evn_stream* stream); 56 | typedef void (evn_stream_on_timeout)(EV_P_ struct evn_stream* stream); 57 | typedef void (evn_stream_on_drain)(EV_P_ struct evn_stream* stream); 58 | typedef void (evn_stream_on_error)(EV_P_ struct evn_stream* stream, struct evn_exception* error); 59 | typedef void (evn_stream_on_close)(EV_P_ struct evn_stream* stream, bool had_error); 60 | 61 | typedef enum 62 | { 63 | evn_CLOSED, 64 | evn_OPEN, 65 | evn_OPENING, 66 | evn_READ_ONLY, 67 | evn_WRITE_ONLY 68 | } evn_ready_state; 69 | 70 | struct evn_exception { 71 | int error_number; 72 | char message[256]; 73 | }; 74 | 75 | struct evn_server { 76 | ev_io io; 77 | EV_P; 78 | evn_server_on_listen* on_listen; 79 | evn_server_on_connection* on_connection; 80 | evn_server_on_close* on_close; 81 | struct sockaddr* socket; 82 | int socket_len; 83 | //array streams; 84 | }; 85 | 86 | struct evn_stream { 87 | ev_io io; 88 | evn_stream_on_connect* on_connect; 89 | evn_stream_on_secure* on_secure; 90 | evn_stream_on_data* on_data; 91 | evn_stream_on_end* on_end; 92 | evn_stream_on_timeout* on_timeout; 93 | evn_stream_on_drain* on_drain; 94 | evn_stream_on_error* on_error; 95 | evn_stream_on_close* on_close; 96 | evn_ready_state ready_state; 97 | int index; 98 | bool oneshot; 99 | evn_bufferlist* bufferlist; 100 | evn_inbuf* _priv_out_buffer; 101 | struct evn_server* server; 102 | struct sockaddr* socket; 103 | int socket_len; 104 | EV_P; 105 | char type; 106 | void* send_data; 107 | }; 108 | 109 | 110 | int evn_set_nonblock(int fd); 111 | 112 | struct evn_server* evn_server_create(EV_P_ evn_server_on_connection* on_connection); 113 | int evn_server_listen(struct evn_server* server, int port, char* address); 114 | void evn_server_priv_on_connection(EV_P_ ev_io *w, int revents); 115 | int evn_server_close(EV_P_ struct evn_server* server); 116 | int evn_server_destroy(EV_P_ struct evn_server* server); 117 | 118 | struct evn_stream* evn_stream_create(int fd); 119 | inline struct evn_stream* evn_create_connection(EV_P_ int port, char* address); 120 | struct evn_stream* evn_create_connection_unix_stream(EV_P_ char* sock_path); 121 | struct evn_stream* evn_create_connection_tcp_stream(EV_P_ int port, char* address); 122 | void evn_stream_priv_on_read(EV_P_ ev_io *w, int revents); 123 | bool evn_stream_write(EV_P_ struct evn_stream* stream, void* data, int size); 124 | bool evn_stream_end(EV_P_ struct evn_stream* stream); 125 | bool evn_stream_destroy(EV_P_ struct evn_stream* stream); 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /ipc/Makefile: -------------------------------------------------------------------------------- 1 | LD_FLAGS = -lev 2 | CC_FLAGS = -Wall -Werror -I../include 3 | CC += $(CC_FLAGS) $(LD_FLAGS) 4 | 5 | all: 6 | $(CC) -o unix-socket-ipc-remote-control unix-socket-ipc-remote-control.c 7 | $(CC) -o unix-socket-ipc-daemon unix-socket-ipc-daemon.c ../obj/array-heap.o 8 | -------------------------------------------------------------------------------- /ipc/my-data.h: -------------------------------------------------------------------------------- 1 | enum NRC_MODE { 2 | NRC_FAST, 3 | NRC_SLOW, 4 | NRC_TURBO, 5 | NRC_REALTIME 6 | }; 7 | 8 | struct my_data { 9 | enum NRC_MODE mode; 10 | char file[255]; 11 | }; 12 | -------------------------------------------------------------------------------- /ipc/unix-socket-ipc-daemon.c: -------------------------------------------------------------------------------- 1 | // Standard Stuff 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Network Stuff 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // Libev 15 | #include 16 | 17 | // Common Logging 18 | //#include 19 | #define g_warning printf 20 | 21 | // Application Specific 22 | #include "array-heap.h" 23 | #include "my-data.h" 24 | 25 | struct my_data data; 26 | 27 | struct sock_ev_serv { 28 | ev_io io; 29 | int fd; 30 | struct sockaddr_un socket; 31 | int socket_len; 32 | array clients; 33 | }; 34 | 35 | struct sock_ev_client { 36 | ev_io io; 37 | int fd; 38 | int index; 39 | struct sock_ev_serv* server; 40 | }; 41 | 42 | int setnonblock(int fd); 43 | static void not_blocked(EV_P_ ev_periodic *w, int revents); 44 | static void server_cb(EV_P_ ev_io *w, int revents); 45 | inline static struct sock_ev_client* client_new(int fd); 46 | static void client_cb(EV_P_ ev_io *w, int revents); 47 | int server_init(struct sock_ev_serv* server, char* sock_path, int max_queue); 48 | int unix_socket_init(struct sockaddr_un* socket_un, char* sock_path, int max_queue); 49 | 50 | 51 | // This callback is called when client data is available 52 | static void client_cb(EV_P_ ev_io *w, int revents) { 53 | // a client has become readable 54 | 55 | struct sock_ev_client* client = (struct sock_ev_client*) w; 56 | 57 | int n; 58 | 59 | printf("[r]"); 60 | n = recv(client->fd, &data, sizeof data, 0); 61 | if (n <= 0) { 62 | if (0 == n) { 63 | // an orderly disconnect 64 | puts("orderly disconnect"); 65 | ev_io_stop(EV_A_ &client->io); 66 | close(client->fd); 67 | } else if (EAGAIN == errno) { 68 | puts("should never get in this state with libev"); 69 | } else { 70 | perror("recv"); 71 | } 72 | return; 73 | } 74 | 75 | // Ping back to let the client know the message was received with success 76 | if (send(client->fd, ".", strlen("."), 0) < 0) { 77 | perror("send"); 78 | } 79 | } 80 | 81 | inline static struct sock_ev_client* client_new(int fd) { 82 | struct sock_ev_client* client; 83 | 84 | client = realloc(NULL, sizeof(struct sock_ev_client)); 85 | client->fd = fd; 86 | //client->server = server; 87 | setnonblock(client->fd); 88 | ev_io_init(&client->io, client_cb, client->fd, EV_READ); 89 | 90 | return client; 91 | } 92 | 93 | // This callback is called when data is readable on the unix socket. 94 | static void server_cb(EV_P_ ev_io *w, int revents) { 95 | puts("unix stream socket has become readable"); 96 | 97 | int client_fd; 98 | struct sock_ev_client* client; 99 | 100 | // since ev_io is the first member, 101 | // watcher `w` has the address of the 102 | // start of the sock_ev_serv struct 103 | struct sock_ev_serv* server = (struct sock_ev_serv*) w; 104 | 105 | while (1) 106 | { 107 | client_fd = accept(server->fd, NULL, NULL); 108 | if( client_fd == -1 ) 109 | { 110 | if( errno != EAGAIN && errno != EWOULDBLOCK ) 111 | { 112 | g_warning("accept() failed errno=%i (%s)", errno, strerror(errno)); 113 | exit(EXIT_FAILURE); 114 | } 115 | break; 116 | } 117 | puts("accepted a client"); 118 | client = client_new(client_fd); 119 | client->server = server; 120 | client->index = array_push(&server->clients, client); 121 | ev_io_start(EV_A_ &client->io); 122 | } 123 | } 124 | 125 | // Simply adds O_NONBLOCK to the file descriptor of choice 126 | int setnonblock(int fd) 127 | { 128 | int flags; 129 | 130 | flags = fcntl(fd, F_GETFL); 131 | flags |= O_NONBLOCK; 132 | return fcntl(fd, F_SETFL, flags); 133 | } 134 | 135 | int unix_socket_init(struct sockaddr_un* socket_un, char* sock_path, int max_queue) { 136 | int fd; 137 | 138 | unlink(sock_path); 139 | 140 | // Setup a unix socket listener. 141 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 142 | if (-1 == fd) { 143 | perror("echo server socket"); 144 | exit(EXIT_FAILURE); 145 | } 146 | 147 | // Set it non-blocking 148 | if (-1 == setnonblock(fd)) { 149 | perror("echo server socket nonblock"); 150 | exit(EXIT_FAILURE); 151 | } 152 | 153 | // Set it as unix socket 154 | socket_un->sun_family = AF_UNIX; 155 | strcpy(socket_un->sun_path, sock_path); 156 | 157 | return fd; 158 | } 159 | 160 | int server_init(struct sock_ev_serv* server, char* sock_path, int max_queue) { 161 | server->fd = unix_socket_init(&server->socket, sock_path, max_queue); 162 | server->socket_len = sizeof(server->socket.sun_family) + strlen(server->socket.sun_path); 163 | 164 | array_init(&server->clients, 128); 165 | 166 | if (-1 == bind(server->fd, (struct sockaddr*) &server->socket, server->socket_len)) 167 | { 168 | perror("echo server bind"); 169 | exit(EXIT_FAILURE); 170 | } 171 | 172 | if (-1 == listen(server->fd, max_queue)) { 173 | perror("listen"); 174 | exit(EXIT_FAILURE); 175 | } 176 | return 0; 177 | } 178 | 179 | int main(void) { 180 | int max_queue = 128; 181 | struct sock_ev_serv server; 182 | struct ev_periodic every_few_seconds; 183 | // Create our single-loop for this single-thread application 184 | EV_P = ev_default_loop(0); 185 | 186 | // Create unix socket in non-blocking fashion 187 | server_init(&server, "/tmp/libev-ipc-daemon.sock", max_queue); 188 | 189 | // To be sure that we aren't actually blocking 190 | ev_periodic_init(&every_few_seconds, not_blocked, 0, 5, 0); 191 | ev_periodic_start(EV_A_ &every_few_seconds); 192 | 193 | // Get notified whenever the socket is ready to read 194 | ev_io_init(&server.io, server_cb, server.fd, EV_READ); 195 | ev_io_start(EV_A_ &server.io); 196 | 197 | // Run our loop, ostensibly forever 198 | puts("unix-socket-echo starting...\n"); 199 | ev_loop(EV_A_ 0); 200 | 201 | // This point is only ever reached if the loop is manually exited 202 | close(server.fd); 203 | return EXIT_SUCCESS; 204 | } 205 | 206 | 207 | static void not_blocked(EV_P_ ev_periodic *w, int revents) { 208 | char mode[20]; 209 | 210 | switch(data.mode) 211 | { 212 | case NRC_TURBO: 213 | strcpy(mode, "turbo"); 214 | break; 215 | case NRC_FAST: 216 | strcpy(mode, "fast"); 217 | break; 218 | case NRC_SLOW: 219 | strcpy(mode, "slow"); 220 | break; 221 | case NRC_REALTIME: 222 | strcpy(mode, "realtime"); 223 | break; 224 | default: 225 | strcpy(mode, "ERROR STATE"); 226 | } 227 | 228 | puts("Current Settings:"); 229 | printf("file: %s\n", data.file); 230 | printf("mode: %s\n", mode); 231 | } 232 | -------------------------------------------------------------------------------- /ipc/unix-socket-ipc-remote-control.c: -------------------------------------------------------------------------------- 1 | // Arg Parser 2 | #include 3 | 4 | // Standard Equipment 5 | #include 6 | #include 7 | 8 | // libev 9 | #include 10 | 11 | // Unix Sockets 12 | #include 13 | #include 14 | #include // fcntl 15 | #include // close 16 | #include 17 | 18 | // Application Specific 19 | #include "my-data.h" 20 | 21 | 22 | /* IPC Data */ 23 | 24 | // NOTE: unless you have a serialize function, you 25 | // may not use a struct with pointers to send across 26 | // a socket. The reference will be meaningless on 27 | // the other side. 28 | struct my_data data; 29 | 30 | 31 | /*********************************** 32 | * 33 | * Remote Control Argument Parser 34 | * 35 | */ 36 | const char* argp_program_version = "Newdora Remote v0.1 (Alpha Alligator)"; 37 | const char* argp_program_bug_address = ""; 38 | 39 | /* Program documentation. */ 40 | static char doc[] = "Newdora Remote -- Remote Control (RC) for Newdora Serving Server Services Streamer (N4S)"; 41 | 42 | /* The options we understand. */ 43 | static struct argp_option options[] = { 44 | {"mode", 'm', "string", 0, "turbo, fast, slow, or realtime"}, 45 | {"file", 'f', "fullpath", 0, "the N4S encoded file to render"}, 46 | { 0 } 47 | }; 48 | 49 | static error_t parse_opt(int key, char* arg, struct argp_state* state) 50 | { 51 | /* Get the input argument from argp_parse, which we 52 | know is a pointer to our arguments structure. */ 53 | struct my_data* data = state->input; 54 | 55 | switch (key) 56 | { 57 | case 'm': 58 | if (0 == strcmp("fast", arg)) 59 | { 60 | data->mode = NRC_FAST; 61 | } 62 | else if (0 == strcmp("slow", arg)) 63 | { 64 | data->mode = NRC_SLOW; 65 | } 66 | else if (0 == strcmp("turbo", arg)) 67 | { 68 | data->mode = NRC_TURBO; 69 | } 70 | else if (0 == strcmp("realtime", arg)) 71 | { 72 | data->mode = NRC_REALTIME; 73 | } 74 | else 75 | { 76 | return EINVAL; 77 | } 78 | break; 79 | 80 | case 'f': 81 | if (strlen(arg) > 255) { 82 | return E2BIG; 83 | } 84 | strcpy(data->file, arg); 85 | break; 86 | 87 | case ARGP_KEY_ARG: 88 | return ARGP_ERR_UNKNOWN; 89 | break; 90 | 91 | case ARGP_KEY_END: 92 | break; 93 | 94 | default: 95 | return ARGP_ERR_UNKNOWN; 96 | } 97 | return 0; 98 | } 99 | 100 | static struct argp argp = { options, parse_opt, 0, doc }; 101 | 102 | 103 | /*********************************** 104 | * 105 | * libev connection client 106 | * 107 | */ 108 | 109 | // Nasty globals for now 110 | // feel free to fork this example and clean it up 111 | EV_P; // Macro for `struct ev_loop* loop` 112 | ev_io daemon_w; 113 | ev_io send_w; 114 | int daemon_fd; 115 | 116 | static void send_cb (EV_P_ ev_io *w, int revents) 117 | { 118 | int s_sent; 119 | 120 | if (revents & EV_WRITE) 121 | { 122 | puts ("daemon ready for writing..."); 123 | 124 | s_sent = send(daemon_fd, &data, sizeof data, 0); 125 | if (-1 == s_sent) 126 | { 127 | perror("echo send"); 128 | exit(EXIT_FAILURE); 129 | } 130 | else if (sizeof data != s_sent) 131 | { 132 | printf("what the heck? mismatch size? crazy!\n"); 133 | } 134 | // once the data is sent, stop notifications that 135 | // data can be sent until there is actually more 136 | // data to send 137 | ev_io_stop(EV_A_ &send_w); 138 | ev_unloop(EV_A_ EVUNLOOP_ALL); 139 | //ev_io_set(&send_w, daemon_fd, EV_READ); 140 | //ev_io_start(EV_A_ &send_w); 141 | } 142 | else if (revents & EV_READ) 143 | { 144 | // TODO ACK / NACK 145 | } 146 | } 147 | 148 | static void daemon_cb (EV_P_ ev_io *w, int revents) 149 | { 150 | puts ("connected, now watching stdin"); 151 | // Once the connection is established, listen for writability 152 | ev_io_start(EV_A_ &send_w); 153 | // Once we're connected, that's the end of that 154 | ev_io_stop(EV_A_ &daemon_w); 155 | } 156 | 157 | 158 | // Simply adds O_NONBLOCK to the file descriptor of choice 159 | int setnonblock(int fd) 160 | { 161 | int flags; 162 | 163 | flags = fcntl(fd, F_GETFL); 164 | flags |= O_NONBLOCK; 165 | return fcntl(fd, F_SETFL, flags); 166 | } 167 | 168 | static void connection_new(EV_P_ char* sock_path) { 169 | int len; 170 | struct sockaddr_un daemon; 171 | 172 | if (-1 == (daemon_fd = socket(AF_UNIX, SOCK_STREAM, 0))) { 173 | perror("socket"); 174 | exit(1); 175 | } 176 | 177 | // Set it non-blocking 178 | if (-1 == setnonblock(daemon_fd)) { 179 | perror("echo client socket nonblock"); 180 | exit(EXIT_FAILURE); 181 | } 182 | 183 | // this should be initialized before the connect() so 184 | // that no packets are dropped when initially sent? 185 | // http://cr.yp.to/docs/connect.html 186 | 187 | // initialize the connect callback so that it starts the stdin asap 188 | ev_io_init (&daemon_w, daemon_cb, daemon_fd, EV_WRITE); 189 | ev_io_start(EV_A_ &daemon_w); 190 | // initialize the send callback, but wait to start until there is data to write 191 | ev_io_init(&send_w, send_cb, daemon_fd, EV_WRITE); 192 | ev_io_start(EV_A_ &send_w); 193 | 194 | daemon.sun_family = AF_UNIX; 195 | strcpy(daemon.sun_path, sock_path); 196 | len = strlen(daemon.sun_path) + sizeof(daemon.sun_family); 197 | 198 | if (-1 == connect(daemon_fd, (struct sockaddr *)&daemon, len)) { 199 | perror("connect"); 200 | //exit(1); 201 | } 202 | } 203 | 204 | int main (int argc, char* argv[]) 205 | { 206 | /* Argument Parsing */ 207 | 208 | // define reasonable defaults 209 | data.mode = NRC_REALTIME; 210 | strcpy(data.file, "/usr/local/media/demo.n4s"); 211 | 212 | // override defaults with user settings 213 | argp_parse (&argp, argc, argv, 0, 0, &data); 214 | 215 | 216 | 217 | /* libev handling */ 218 | 219 | loop = EV_DEFAULT; 220 | connection_new(EV_A_ "/tmp/libev-ipc-daemon.sock"); 221 | 222 | // now wait for events to arrive 223 | ev_loop(EV_A_ 0); 224 | 225 | // doesn't get here unless unloop is called 226 | return 0; 227 | } 228 | 229 | -------------------------------------------------------------------------------- /obj/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolaj86/libev-examples/48632c7e714d4b61122a05fab30906ea90f2d51b/obj/.gitignore -------------------------------------------------------------------------------- /paired-threaded-ipc/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -Werror -Iinclude -g3 -ggdb3 -I/opt/local/include -I../include 2 | LDFLAGS=-lev -levnet -lpthread -g3 -ggdb3 -L/opt/local/lib -L./lib 3 | # on OS X include -largp 4 | # LDFLAGS+= -largp 5 | CC=gcc ${CFLAGS} ${LDFLAGS} 6 | 7 | all: enet clean server remote test 8 | 9 | enet: 10 | @mkdir -p ./lib/ 11 | make -C ../libevnet 12 | cp -a ../libevnet/lib/libevnet.a ./lib/ 13 | cp -a ../libevnet/lib/libevnet.so* ./lib/ 14 | 15 | server: 16 | $(CC) -o dummyd dummy-daemon.c dummy-worker.c dummy-worker-thread.c dummy-settings.c rand.c -levnet -L./lib 17 | 18 | remote: 19 | $(CC) -o dummy-rc dummy-rc.c dummy-settings.c -levnet -L./lib 20 | 21 | test: 22 | $(CC) -o rand-test rand.c rand-test.c 23 | $(CC) -o dummy-worker-test dummy-worker-test.c dummy-worker.c rand.c 24 | 25 | clean: 26 | @rm -rf *.dSYM dummyd dummy-rc *-test 27 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-daemon.c: -------------------------------------------------------------------------------- 1 | #include //the command line argument parser 2 | #include //setpriority 3 | #include //signals to process data and quit cleanly 4 | 5 | #include "dummy-worker-thread.h" 6 | 7 | // Standard Stuff 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Libev 14 | #include 15 | #include 16 | 17 | #include "dummy-settings.h" 18 | #include "bool.h" 19 | 20 | #include "evn.h" 21 | 22 | #define LOG_PATH "/tmp/" 23 | 24 | // to move out 25 | static void dwt_enqueue(char* filename); 26 | 27 | // Function Prototypes 28 | void clean_shutdown(EV_P_ int sig); 29 | static void test_process_is_not_blocked(EV_P_ ev_periodic *w, int revents); 30 | 31 | // EVN Network Callbacks 32 | static void stream_on_data(EV_P_ struct evn_stream* stream, void* data, int n); 33 | static void stream_on_close(EV_P_ struct evn_stream* stream, bool had_error); 34 | static void server_on_connection(EV_P_ struct evn_server* server, struct evn_stream* stream); 35 | 36 | // Help / Docs 37 | const char* argp_program_version = "ACME DummyServe v1.0 (Alphabet Animal)"; 38 | const char* argp_program_bug_address = ""; 39 | 40 | /* Program documentation. */ 41 | static char doc[] = "ACME DummyServe -- An abstraction layer between the Dummy Processing Algorithms and Business Logic"; 42 | 43 | //settings structs 44 | static DUMMY_SETTINGS dummy_settings; 45 | static bool redirect = false; 46 | 47 | static pthread_t dummy_worker_pthread; 48 | static struct DUMMY_WORKER_THREAD_CONTROL thread_control; 49 | 50 | 51 | /* The options we understand. */ 52 | static struct argp_option options[] = { 53 | #include "dummy_settings_argp_option.c" 54 | {"write-logs" , 'w', 0 , 0, "redirect stdout and stderr to log files" }, 55 | { 0 } 56 | }; 57 | 58 | /* Parse a single option. */ 59 | static error_t parse_opt (int key, char *arg, struct argp_state *state) 60 | { 61 | switch (key) 62 | { 63 | #include "dummy_settings_parse_opt.c" 64 | 65 | case 'w': 66 | redirect = true; 67 | break; 68 | 69 | case ARGP_KEY_ARG: 70 | return ARGP_ERR_UNKNOWN; 71 | 72 | case ARGP_KEY_END: 73 | break; 74 | 75 | default: 76 | return ARGP_ERR_UNKNOWN; 77 | } 78 | return 0; 79 | } 80 | 81 | // Our argument parser. 82 | static struct argp argp = { options, parse_opt, 0, doc }; 83 | 84 | void clean_shutdown(EV_P_ int sig) { 85 | void* exit_status; 86 | 87 | puts("[Daemon] Shutting Down.\n"); 88 | fprintf(stderr, "Received signal %d, shutting down\n", sig); 89 | // TODO handle signal close(server.fd); 90 | ev_async_send(thread_control.EV_A, &(thread_control.cleanup)); 91 | 92 | //this will block until the dummy_worker_pthread exits, at which point we will continue with out shutdown. 93 | pthread_join(dummy_worker_pthread, &exit_status); 94 | ev_loop_destroy (EV_DEFAULT_UC); 95 | puts("[dummyd] Dummy cleanup finished."); 96 | exit(EXIT_SUCCESS); 97 | } 98 | 99 | void redirect_output() 100 | { 101 | // these lines are to direct the stdout and stderr to log files we can access even when run as a daemon (after the possible help info is displayed.) 102 | //open up the files we want to use for out logs 103 | int new_stderr, new_stdout; 104 | new_stderr = open(LOG_PATH "dummy_error.log", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 105 | new_stdout = open(LOG_PATH "dummy_debug.log", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 106 | 107 | //truncate those files to clear the old data from long since past. 108 | if (0 != ftruncate(new_stderr, 0)) 109 | { 110 | perror("could not truncate stderr log"); 111 | } 112 | if (0 != ftruncate(new_stdout, 0)) 113 | { 114 | perror("could not truncate stdout log"); 115 | } 116 | 117 | //duplicate the new file descriptors and assign the file descriptors 1 and 2 (stdout and stderr) to the duplicates 118 | dup2(new_stderr, 2); 119 | dup2(new_stdout, 1); 120 | 121 | //now that they are duplicated we can close them and let the overhead c stuff worry about closing stdout and stderr later. 122 | close(new_stderr); 123 | close(new_stdout); 124 | } 125 | 126 | int main (int argc, char* argv[]) 127 | { 128 | // Create our single-loop for this single-thread application 129 | EV_P; 130 | pthread_attr_t attr; 131 | int thread_status; 132 | struct evn_server* server; 133 | char socket_address[256]; 134 | EV_A = ev_default_loop(0); 135 | 136 | 137 | // Set default options, then parse new arguments to update the settings 138 | dummy_settings_set_presets(&dummy_settings); 139 | argp_parse (&argp, argc, argv, 0, 0, &dummy_settings); 140 | // TODO separate worker settings from daemon settings 141 | // (i.e. redirect) 142 | 143 | if (true == redirect) 144 | { 145 | redirect_output(); 146 | } 147 | 148 | if (0) 149 | { 150 | struct ev_periodic every_few_seconds; 151 | 152 | // To be sure that we aren't actually blocking 153 | ev_periodic_init(&every_few_seconds, test_process_is_not_blocked, 0, 1, 0); 154 | ev_periodic_start(EV_A_ &every_few_seconds); 155 | } 156 | 157 | // Set the priority of this whole process higher (requires root) 158 | setpriority(PRIO_PROCESS, 0, -13); // -15 159 | 160 | // initialize the values of the struct that we will be giving to the new thread 161 | thread_control.dummy_settings = &dummy_settings; 162 | thread_control.buffer_head = 0; 163 | thread_control.buffer_count = 0; 164 | thread_control.EV_A = ev_loop_new(EVFLAG_AUTO); 165 | 166 | pthread_attr_init(&attr); 167 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 168 | // Initialize the thread that will manage the DUMMY_WORKER 169 | thread_status = pthread_create(&dummy_worker_pthread, &attr, dummy_worker_thread, (void *)(&thread_control)); 170 | if (0 != thread_status) 171 | { 172 | fprintf(stderr, "thread creation failed with errno %d (%s)\n", thread_status, strerror(thread_status)); 173 | exit(EXIT_FAILURE); 174 | } 175 | pthread_attr_destroy(&attr); 176 | 177 | 178 | // Create unix socket in non-blocking fashion 179 | snprintf(socket_address, sizeof(socket_address), DUMMYD_SOCK, (int)getuid()); 180 | unlink(socket_address); 181 | server = evn_server_create(EV_A_ server_on_connection); 182 | server->on_connection = server_on_connection; 183 | evn_server_listen(server, 0, socket_address); 184 | 185 | // Run our loop, until we recieve the QUIT, TERM or INT signals, or an 'x' over the socket. 186 | puts("[Daemon] Looping.\n"); 187 | ev_loop(EV_A_ 0); 188 | 189 | // Cleanup if `unloop` is ever called 190 | clean_shutdown(EV_A_ 0); 191 | return 0; 192 | } 193 | 194 | static void test_process_is_not_blocked(EV_P_ ev_periodic *w, int revents) { 195 | static int up_time = 0; 196 | struct stat sb; 197 | int status1, status2; 198 | 199 | //keep the stdout log file from getting too big and crashing the system after a long time running. 200 | //we are in big trouble anyway if the stderr file gets that big. 201 | fstat(1, &sb); 202 | if(sb.st_size > 1048576) 203 | { 204 | status1 = ftruncate(1, 0); 205 | status2 = lseek(1, 0, SEEK_SET); 206 | if ( (0 != status1) || (0 != status2)) 207 | { 208 | perror("truncating stdout file"); 209 | } 210 | else 211 | { 212 | printf("successfully truncated the stdout file\n"); 213 | } 214 | } 215 | 216 | printf("I'm still alive (%d)\n", up_time); 217 | up_time += 1; 218 | } 219 | 220 | static void dwt_enqueue(char* filename) 221 | { 222 | int buffer_tail; 223 | 224 | pthread_mutex_lock(&(thread_control.buffer_lock)); 225 | if (thread_control.buffer_count < BUFFER_SIZE) 226 | { 227 | buffer_tail = (thread_control.buffer_head + thread_control.buffer_count) % BUFFER_SIZE; 228 | strncpy(thread_control.buffer[buffer_tail], filename, sizeof thread_control.buffer[buffer_tail]); 229 | thread_control.buffer_count += 1; 230 | printf("added to the buffer. now contains %d\n", thread_control.buffer_count); 231 | } 232 | else 233 | { 234 | //buffer is full, overwrite the oldest entry and move the head to the next oldest one 235 | buffer_tail = thread_control.buffer_head; 236 | thread_control.buffer_head = (thread_control.buffer_head + 1) % BUFFER_SIZE; 237 | printf("overwriting %s in the buffer\n", thread_control.buffer[buffer_tail]); 238 | strncpy(thread_control.buffer[buffer_tail], filename, sizeof thread_control.buffer[buffer_tail]); 239 | } 240 | pthread_mutex_unlock(&(thread_control.buffer_lock)); 241 | 242 | ev_async_send(thread_control.EV_A, &(thread_control.process_data)); 243 | } 244 | 245 | // When writing these settings we must lock 246 | inline void dwt_update_settings(void* data) 247 | { 248 | // tell the DUMMY_WORKER thread to copy the settings from the pointers we gave it 249 | pthread_mutex_lock(&(thread_control.settings_lock)); 250 | memcpy(&dummy_settings, data, sizeof(dummy_settings)); 251 | pthread_mutex_unlock(&(thread_control.settings_lock)); 252 | ev_async_send(thread_control.EV_A, &(thread_control.update_settings)); 253 | } 254 | 255 | 256 | static void server_on_connection(EV_P_ struct evn_server* server, struct evn_stream* stream) 257 | { 258 | puts("[Server CB] accepted a stream"); 259 | stream->on_data = stream_on_data; 260 | stream->on_close = stream_on_close; 261 | stream->server = server; 262 | stream->oneshot = true; 263 | } 264 | 265 | static void stream_on_close(EV_P_ struct evn_stream* stream, bool had_error) 266 | { 267 | puts("[Stream CB] Close"); 268 | } 269 | 270 | // This callback is called when stream data is available 271 | static void stream_on_data(EV_P_ struct evn_stream* stream, void* raw, int n) 272 | { 273 | char filename[32]; 274 | char code = ((char*)raw)[0]; 275 | void* data = (void*) &((char*)raw)[1]; 276 | n -= 1; 277 | 278 | stream->type = code; 279 | 280 | printf("[Data CB] - stream has become readable (%d bytes)\n", n); 281 | 282 | switch (stream->type) { 283 | case 's': 284 | printf("\t's' - update dummy_settings"); 285 | 286 | if (sizeof(dummy_settings) != n) { 287 | printf("expected=%d actual=%d", (int) sizeof(dummy_settings), n); 288 | return; 289 | } 290 | 291 | dwt_update_settings(data); 292 | 293 | break; 294 | 295 | 296 | case '.': 297 | printf("\t'.' - continue working"); 298 | 299 | 300 | // TODO dummy data 301 | if (sizeof(filename) != n) { 302 | printf("expected=%d actual=%d", (int) sizeof(filename), n); 303 | return; 304 | } 305 | 306 | memset(filename, 0, sizeof(filename)); 307 | memcpy(filename, data, sizeof(filename)); 308 | 309 | dwt_enqueue(filename); 310 | break; 311 | 312 | 313 | case 'x': 314 | printf("\t'x' - received kill message; exiting"); 315 | 316 | if (0 != n) { 317 | printf("expected=%d actual=%d", (int) 0, n); 318 | return; 319 | } 320 | 321 | ev_unloop(EV_A_ EVUNLOOP_ALL); 322 | break; 323 | 324 | 325 | default: 326 | printf("\t'%c' (%d) - received unknown message; exiting", stream->type, stream->type); 327 | 328 | ev_unloop(EV_A_ EVUNLOOP_ALL); 329 | } 330 | 331 | free(raw); 332 | puts("."); 333 | } 334 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-rc.c: -------------------------------------------------------------------------------- 1 | // Arg Parser 2 | #include 3 | #include 4 | 5 | // Standard Equipment 6 | #include 7 | #include 8 | 9 | // libev 10 | #include 11 | 12 | // Unix Sockets 13 | #include 14 | #include 15 | #include // fcntl 16 | #include // close 17 | #include 18 | 19 | // Application Specific 20 | #include "dummy-settings.h" 21 | #include "bool.h" 22 | 23 | #include "evn.h" 24 | 25 | /* IPC Data */ 26 | 27 | // NOTE: you may not use a struct with pointers to send across 28 | // a socket. The reference will be meaningless on the other side. 29 | static DUMMY_SETTINGS dummy_settings; 30 | static bool kill_process = false; 31 | static bool trigger_process = false; 32 | static char filename[32] = {}; 33 | 34 | 35 | /*********************************** 36 | * 37 | * Remote Control Argument Parser 38 | * 39 | */ 40 | const char* argp_program_version = "ACME DummyRemote v1.0 (Alphabet Animal)"; 41 | const char* argp_program_bug_address = ""; 42 | 43 | /* Program documentation. */ 44 | static char doc[] = "ACME DummyRemote -- A unix-stream client for controlling DummyServe"; 45 | 46 | /* The options we understand. */ 47 | static struct argp_option options[] = { 48 | #include "dummy_settings_argp_option.c" 49 | {"kill" , 'x', 0 , 0, "send the kill command over the socket"}, 50 | {"run" , '.', "file" , 0, "send the run command over the socket with the file to process (only the file name not the path. must be in /dev/shm)"}, 51 | { 0 } 52 | }; 53 | 54 | /* Parse a single option. */ 55 | static error_t parse_opt (int key, char *arg, struct argp_state *state) 56 | { 57 | switch (key) 58 | { 59 | case 'x': 60 | kill_process = true; 61 | break; 62 | case '.': 63 | trigger_process = true; 64 | strncpy(filename, arg, sizeof filename); //strncpy to make sure we don't exceed the bounds of our array. 65 | filename[sizeof filename - 1] = '\0'; //just make sure the result is NULL terminated 66 | break; 67 | #include "dummy_settings_parse_opt.c" 68 | 69 | case ARGP_KEY_ARG: 70 | return ARGP_ERR_UNKNOWN; 71 | 72 | case ARGP_KEY_END: 73 | break; 74 | 75 | default: 76 | return ARGP_ERR_UNKNOWN; 77 | } 78 | return 0; 79 | } 80 | 81 | static struct argp argp = { options, parse_opt, 0, doc }; 82 | 83 | // 84 | // Client Callbacks 85 | // 86 | 87 | static void on_close(EV_P_ struct evn_stream* stream, bool had_error) 88 | { 89 | puts("[dummy-rc]\n\tClose CB"); 90 | } 91 | 92 | static void on_drain(EV_P_ struct evn_stream* stream) 93 | { 94 | bool all_sent; 95 | puts("[dummy-rc]\n\tnow ready for writing..."); 96 | 97 | if (true == kill_process) 98 | { 99 | all_sent = evn_stream_write(EV_A_ stream, "x", sizeof(char)); 100 | } 101 | else if (true == trigger_process) 102 | { 103 | evn_stream_write(EV_A_ stream, ".", sizeof(char)); 104 | all_sent = evn_stream_write(EV_A_ stream, filename, sizeof(filename)); 105 | } 106 | else // send settings 107 | { 108 | evn_stream_write(EV_A_ stream, "s", sizeof(char)); 109 | all_sent = evn_stream_write(EV_A_ stream, &dummy_settings, sizeof(dummy_settings)); 110 | } 111 | 112 | // 113 | if (false == all_sent) 114 | { 115 | puts("[dummy-rc]"); 116 | puts("\tall or part of the data was queued in user memory"); 117 | puts("\t'drain' will be emitted when the buffer is again free"); 118 | } 119 | else 120 | { 121 | puts("[dummy-rc]\n\tSuccess: wrote all data."); 122 | } 123 | 124 | evn_stream_end(EV_A_ stream); 125 | } 126 | 127 | int main (int argc, char* argv[]) 128 | { 129 | //set ALL the default values 130 | dummy_settings_set_presets(&dummy_settings); 131 | 132 | // Parse our arguments; every option seen by parse_opt will be reflected in the globals 133 | argp_parse (&argp, argc, argv, 0, 0, &dummy_settings); 134 | 135 | // libev handling 136 | EV_P = EV_DEFAULT; 137 | 138 | //evn_create_connection(EV_A_ "/tmp/libev-ipc-daemon.sock"); 139 | char socket_address[256]; 140 | snprintf(socket_address, sizeof socket_address, DUMMYD_SOCK, (int)getuid()); 141 | struct evn_stream* stream = evn_create_connection(EV_A_ 0, socket_address); 142 | if (stream) { 143 | stream->on_close = on_close; 144 | stream->on_drain = on_drain; 145 | } 146 | 147 | // now wait for events to arrive 148 | ev_loop(EV_A_ 0); 149 | 150 | // doesn't get here unless unloop is called 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-rc.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function () { 3 | var net = require('net'), 4 | stream; 5 | 6 | stream = net.createConnection("/tmp/dummyd-504.sock"); 7 | stream.on("connect", function () { 8 | console.log("connected"); 9 | stream.write(".Hello World! "); 10 | stream.end(); 11 | }); 12 | }()); 13 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-settings.c: -------------------------------------------------------------------------------- 1 | #include // printf 2 | 3 | #include "dummy-settings.h" 4 | 5 | void dummy_settings_set_presets(DUMMY_SETTINGS* dummy_settings) 6 | { 7 | //set ALL the default values 8 | dummy_settings->delay = 100; 9 | dummy_settings->variance = 10; 10 | dummy_settings->init_time = 10; 11 | } 12 | 13 | void dummy_settings_print(DUMMY_SETTINGS* dummy_settings) 14 | { 15 | printf("delay = %dms\n", dummy_settings->delay); 16 | printf("variance = %d%%\n", dummy_settings->variance); 17 | printf("init_time = %d%%\n", dummy_settings->init_time); 18 | } 19 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-worker-test.c: -------------------------------------------------------------------------------- 1 | #include // printf 2 | #include // gettimeofday 3 | 4 | #include "bool.h" 5 | #include "loop.h" 6 | #include "dummy-worker.h" 7 | 8 | static DUMMY_SETTINGS settings; 9 | 10 | void 11 | time_delta_print(char* prefix) 12 | { 13 | static int count; 14 | static struct timeval last_timep = {}; 15 | struct timeval timep; 16 | int delta; 17 | 18 | if (!last_timep.tv_usec) 19 | { 20 | gettimeofday(&last_timep, NULL); 21 | return; 22 | } 23 | 24 | gettimeofday(&timep, NULL); 25 | 26 | delta = (int) (timep.tv_usec - last_timep.tv_usec); 27 | if (delta < 0) 28 | { 29 | delta += 1000000; 30 | } 31 | printf("%3d %s: %.3f ms\n", count, prefix, delta / 1000.0); 32 | 33 | last_timep = timep; 34 | count += 1; 35 | } 36 | 37 | 38 | void 39 | work() 40 | { 41 | static char data[4096 * 1024]; 42 | int i = 0; 43 | 44 | loop { 45 | if (i >= 10) 46 | { 47 | return; 48 | } 49 | worker_run(&data); 50 | time_delta_print("run"); 51 | i += 1; 52 | } 53 | } 54 | 55 | 56 | int 57 | main (int argc, char* argv[]) 58 | { 59 | settings.init_time = 10; 60 | settings.delay = 128; 61 | settings.variance = 15; 62 | 63 | time_delta_print("begin"); 64 | 65 | worker_init(&settings); 66 | time_delta_print("init"); 67 | 68 | worker_set(&settings); 69 | time_delta_print("set"); 70 | 71 | work(); 72 | 73 | worker_clean(); 74 | time_delta_print("clean"); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-worker-thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "dummy-worker-thread.h" 7 | #include "dummy-worker.h" 8 | #include "dummy-settings.h" 9 | 10 | //function prototypes 11 | static void update_settings(EV_P_ ev_async *w, int revents); 12 | static void process_data(EV_P_ ev_async *w, int revents); 13 | static void cleanup(EV_P_ ev_async *w, int revents); 14 | 15 | //the pointer the the control struct in the thread that we 16 | static struct DUMMY_WORKER_THREAD_CONTROL* thread_control; 17 | 18 | //our own copies of the settings structs that we give to the DUMMY_WORKER so it doesn't have to worry about mutex locks. 19 | static DUMMY_SETTINGS dummy_settings; 20 | 21 | void* dummy_worker_thread(void* control_struct) 22 | { 23 | thread_control = (struct DUMMY_WORKER_THREAD_CONTROL*)control_struct; 24 | 25 | pthread_mutex_lock(&(thread_control->settings_lock)); 26 | memcpy(&dummy_settings, thread_control->dummy_settings, sizeof dummy_settings); 27 | pthread_mutex_unlock(&(thread_control->settings_lock)); 28 | 29 | // Initialize the codec 30 | if(EXIT_SUCCESS != worker_init(&dummy_settings)) { 31 | fprintf(stderr, "failed init DUMMY_WORKER algorithm\n"); 32 | exit(EXIT_FAILURE); 33 | } 34 | 35 | //now set the watchers for the appropriate action. 36 | ev_async_init (&(thread_control->update_settings), &update_settings); 37 | ev_async_start(thread_control->EV_A, &(thread_control->update_settings)); 38 | 39 | ev_async_init (&(thread_control->process_data), &process_data); 40 | ev_async_start(thread_control->EV_A, &(thread_control->process_data)); 41 | 42 | ev_async_init (&(thread_control->cleanup), &cleanup); 43 | ev_async_start(thread_control->EV_A, &(thread_control->cleanup)); 44 | 45 | ev_loop(thread_control->EV_A, 0); 46 | 47 | fprintf(stderr, "the event loop for the DUMMY_WORKER thread exitted\n"); 48 | return NULL; 49 | } 50 | 51 | static void update_settings(EV_P_ ev_async *w, int revents) 52 | { 53 | pthread_mutex_lock(&(thread_control->settings_lock)); 54 | memcpy(&dummy_settings, thread_control->dummy_settings, sizeof dummy_settings); 55 | pthread_mutex_unlock(&(thread_control->settings_lock)); 56 | 57 | worker_set(&dummy_settings); 58 | } 59 | 60 | static void process_data(EV_P_ ev_async *w, int revents) 61 | { 62 | static char current_file[32]; 63 | 64 | pthread_mutex_lock(&(thread_control->buffer_lock)); 65 | strncpy(current_file, thread_control->buffer[thread_control->buffer_head], sizeof current_file); 66 | thread_control->buffer_head = (thread_control->buffer_head + 1) % BUFFER_SIZE; 67 | thread_control->buffer_count -= 1; 68 | pthread_mutex_unlock(&(thread_control->buffer_lock)); 69 | 70 | worker_run(current_file); 71 | } 72 | 73 | static void cleanup(EV_P_ ev_async *w, int revents) 74 | { 75 | worker_clean(); 76 | ev_unloop(EV_A_ EVUNLOOP_ALL); 77 | } 78 | -------------------------------------------------------------------------------- /paired-threaded-ipc/dummy-worker.c: -------------------------------------------------------------------------------- 1 | #include // usleep 2 | #include // perror 3 | #include // memcpy 4 | 5 | 6 | #include "dummy-worker.h" 7 | #include "rand.h" 8 | 9 | static DUMMY_SETTINGS settings; 10 | 11 | static void msleep(int time) { 12 | if (0 != usleep((useconds_t) (time * 1000))) 13 | { 14 | perror("couldn't usleep (possibly caught a signal)"); 15 | } 16 | } 17 | 18 | int 19 | worker_init(DUMMY_SETTINGS* new_settings) 20 | { 21 | worker_set(new_settings); 22 | msleep(settings.init_time); 23 | return 0; 24 | } 25 | 26 | void 27 | worker_run(void* data) 28 | { 29 | int proc_time; 30 | 31 | if (settings.variance) 32 | { 33 | proc_time = random_in_range_percent(settings.delay, (float) settings.variance); 34 | } 35 | else 36 | { 37 | proc_time = settings.delay; 38 | } 39 | 40 | msleep(proc_time); 41 | } 42 | 43 | void 44 | worker_clean() 45 | { 46 | msleep(settings.init_time); 47 | } 48 | 49 | void 50 | worker_set(DUMMY_SETTINGS* new_settings) 51 | { 52 | memcpy((void*) &settings, (void*) new_settings, (size_t) sizeof(DUMMY_SETTINGS)); 53 | } 54 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/dummy-settings.h: -------------------------------------------------------------------------------- 1 | #ifndef DUMMY_SETTINGS_STRUCT_H 2 | #define DUMMY_SETTINGS_STRUCT_H 3 | 4 | #define DUMMYD_SOCK "/tmp/dummyd-%d.sock" 5 | 6 | #pragma pack(1) 7 | typedef struct 8 | { 9 | int delay; // how long (ms) to fake processing time 10 | int variance; // percentage of variance on fake processing time 11 | int init_time; 12 | } DUMMY_SETTINGS; 13 | #pragma pack() 14 | 15 | void dummy_settings_set_presets(DUMMY_SETTINGS*); 16 | void dummy_settings_print(DUMMY_SETTINGS*); 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/dummy-worker-thread.h: -------------------------------------------------------------------------------- 1 | #ifndef DUMMY_WORKER_THREAD_STRUCT 2 | #define DUMMY_WORKER_THREAD_STRUCT 3 | 4 | #include 5 | #include 6 | 7 | #include "dummy-settings.h" 8 | 9 | // this is the struct containing all the variables that the main thread needs to communicate with the DUMMY_WORKER thread 10 | // we do this so we can easily pass the info into the secondary thread without using any convoluting externs 11 | #define BUFFER_SIZE 6 12 | 13 | struct DUMMY_WORKER_THREAD_CONTROL { 14 | //the buffer and the mutex lock that prevent multiple read/writes at the same time. 15 | pthread_mutex_t buffer_lock; 16 | char buffer[BUFFER_SIZE][32]; // filename 17 | int buffer_count; 18 | int buffer_head; 19 | 20 | //pointers to settings structs and the mutex lock to protect them 21 | pthread_mutex_t settings_lock; 22 | DUMMY_SETTINGS* dummy_settings; 23 | 24 | //the async handlers libev provides to communicate between threads. 25 | ev_async update_settings; 26 | ev_async process_data; 27 | ev_async cleanup; 28 | 29 | // the actual event loop used to talk across threads. 30 | EV_P; 31 | }; 32 | 33 | void* dummy_worker_thread(void* control_struct); 34 | #endif 35 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/dummy-worker.h: -------------------------------------------------------------------------------- 1 | #ifndef _DUMMY_WORKER_H_ 2 | #define _DUMMY_WORKER_H_ 3 | 4 | #include "dummy-settings.h" 5 | 6 | int 7 | worker_init(DUMMY_SETTINGS* new_settings); 8 | 9 | void 10 | worker_run(void* data); 11 | 12 | void 13 | worker_clean(); 14 | 15 | void 16 | worker_set(DUMMY_SETTINGS* new_settings); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/dummy_settings_argp_option.c: -------------------------------------------------------------------------------- 1 | {"processing-time" , 't', "ms" , 0, "how long (ms) to wait to simulate processing time (1000ms default)" }, 2 | {"percent-variance" , 'p', "\%" , 0, "variance (\%) in processing time (10 default)" }, 3 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/dummy_settings_parse_opt.c: -------------------------------------------------------------------------------- 1 | case 't': 2 | dummy_settings.delay = atoi(arg); 3 | break; 4 | case 'p': 5 | dummy_settings.variance = atoi(arg); 6 | break; 7 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/ev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libev native API header 3 | * 4 | * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef EV_H_ 41 | #define EV_H_ 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | typedef double ev_tstamp; 48 | 49 | /* these priorities are inclusive, higher priorities will be called earlier */ 50 | #ifndef EV_MINPRI 51 | # define EV_MINPRI -2 52 | #endif 53 | #ifndef EV_MAXPRI 54 | # define EV_MAXPRI +2 55 | #endif 56 | 57 | #ifndef EV_MULTIPLICITY 58 | # define EV_MULTIPLICITY 1 59 | #endif 60 | 61 | #ifndef EV_PERIODIC_ENABLE 62 | # define EV_PERIODIC_ENABLE 1 63 | #endif 64 | 65 | #ifndef EV_STAT_ENABLE 66 | # define EV_STAT_ENABLE 1 67 | #endif 68 | 69 | #ifndef EV_IDLE_ENABLE 70 | # define EV_IDLE_ENABLE 1 71 | #endif 72 | 73 | #ifndef EV_FORK_ENABLE 74 | # define EV_FORK_ENABLE 1 75 | #endif 76 | 77 | #ifndef EV_EMBED_ENABLE 78 | # define EV_EMBED_ENABLE 1 79 | #endif 80 | 81 | #ifndef EV_ASYNC_ENABLE 82 | # define EV_ASYNC_ENABLE 1 83 | #endif 84 | 85 | #ifndef EV_WALK_ENABLE 86 | # define EV_WALK_ENABLE 0 /* not yet */ 87 | #endif 88 | 89 | #ifndef EV_ATOMIC_T 90 | # include 91 | # define EV_ATOMIC_T sig_atomic_t volatile 92 | #endif 93 | 94 | /*****************************************************************************/ 95 | 96 | #if EV_STAT_ENABLE 97 | # ifdef _WIN32 98 | # include 99 | # include 100 | # endif 101 | # include 102 | #endif 103 | 104 | /* support multiple event loops? */ 105 | #if EV_MULTIPLICITY 106 | struct ev_loop; 107 | # define EV_P struct ev_loop *loop 108 | # define EV_P_ EV_P, 109 | # define EV_A loop 110 | # define EV_A_ EV_A, 111 | # define EV_DEFAULT_UC ev_default_loop_uc () 112 | # define EV_DEFAULT_UC_ EV_DEFAULT_UC, 113 | # define EV_DEFAULT ev_default_loop (0) 114 | # define EV_DEFAULT_ EV_DEFAULT, 115 | #else 116 | # define EV_P void 117 | # define EV_P_ 118 | # define EV_A 119 | # define EV_A_ 120 | # define EV_DEFAULT 121 | # define EV_DEFAULT_ 122 | # define EV_DEFAULT_UC 123 | # define EV_DEFAULT_UC_ 124 | # undef EV_EMBED_ENABLE 125 | #endif 126 | 127 | #if __STDC_VERSION__ >= 199901L || __GNUC__ >= 3 128 | # define EV_INLINE static inline 129 | #else 130 | # define EV_INLINE static 131 | #endif 132 | 133 | /*****************************************************************************/ 134 | 135 | /* eventmask, revents, events... */ 136 | #define EV_UNDEF -1 /* guaranteed to be invalid */ 137 | #define EV_NONE 0x00 /* no events */ 138 | #define EV_READ 0x01 /* ev_io detected read will not block */ 139 | #define EV_WRITE 0x02 /* ev_io detected write will not block */ 140 | #define EV__IOFDSET 0x80 /* internal use only */ 141 | #define EV_IO EV_READ /* alias for type-detection */ 142 | #define EV_TIMEOUT 0x00000100 /* timer timed out */ 143 | #define EV_TIMER EV_TIMEOUT /* alias for type-detection */ 144 | #define EV_PERIODIC 0x00000200 /* periodic timer timed out */ 145 | #define EV_SIGNAL 0x00000400 /* signal was received */ 146 | #define EV_CHILD 0x00000800 /* child/pid had status change */ 147 | #define EV_STAT 0x00001000 /* stat data changed */ 148 | #define EV_IDLE 0x00002000 /* event loop is idling */ 149 | #define EV_PREPARE 0x00004000 /* event loop about to poll */ 150 | #define EV_CHECK 0x00008000 /* event loop finished poll */ 151 | #define EV_EMBED 0x00010000 /* embedded event loop needs sweep */ 152 | #define EV_FORK 0x00020000 /* event loop resumed in child */ 153 | #define EV_ASYNC 0x00040000 /* async intra-loop signal */ 154 | #define EV_CUSTOM 0x01000000 /* for use by user code */ 155 | #define EV_ERROR 0x80000000 /* sent when an error occurs */ 156 | 157 | /* can be used to add custom fields to all watchers, while losing binary compatibility */ 158 | #ifndef EV_COMMON 159 | # define EV_COMMON void *data; 160 | #endif 161 | #ifndef EV_PROTOTYPES 162 | # define EV_PROTOTYPES 1 163 | #endif 164 | 165 | #define EV_VERSION_MAJOR 3 166 | #define EV_VERSION_MINOR 9 167 | 168 | #ifndef EV_CB_DECLARE 169 | # define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents); 170 | #endif 171 | #ifndef EV_CB_INVOKE 172 | # define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents)) 173 | #endif 174 | 175 | /* 176 | * struct member types: 177 | * private: you may look at them, but not change them, 178 | * and they might not mean anything to you. 179 | * ro: can be read anytime, but only changed when the watcher isn't active. 180 | * rw: can be read and modified anytime, even when the watcher is active. 181 | * 182 | * some internal details that might be helpful for debugging: 183 | * 184 | * active is either 0, which means the watcher is not active, 185 | * or the array index of the watcher (periodics, timers) 186 | * or the array index + 1 (most other watchers) 187 | * or simply 1 for watchers that aren't in some array. 188 | * pending is either 0, in which case the watcher isn't, 189 | * or the array index + 1 in the pendings array. 190 | */ 191 | 192 | #if EV_MINPRI == EV_MAXPRI 193 | # define EV_DECL_PRIORITY 194 | #else 195 | # define EV_DECL_PRIORITY int priority; 196 | #endif 197 | 198 | /* shared by all watchers */ 199 | #define EV_WATCHER(type) \ 200 | int active; /* private */ \ 201 | int pending; /* private */ \ 202 | EV_DECL_PRIORITY /* private */ \ 203 | EV_COMMON /* rw */ \ 204 | EV_CB_DECLARE (type) /* private */ 205 | 206 | #define EV_WATCHER_LIST(type) \ 207 | EV_WATCHER (type) \ 208 | struct ev_watcher_list *next; /* private */ 209 | 210 | #define EV_WATCHER_TIME(type) \ 211 | EV_WATCHER (type) \ 212 | ev_tstamp at; /* private */ 213 | 214 | /* base class, nothing to see here unless you subclass */ 215 | typedef struct ev_watcher 216 | { 217 | EV_WATCHER (ev_watcher) 218 | } ev_watcher; 219 | 220 | /* base class, nothing to see here unless you subclass */ 221 | typedef struct ev_watcher_list 222 | { 223 | EV_WATCHER_LIST (ev_watcher_list) 224 | } ev_watcher_list; 225 | 226 | /* base class, nothing to see here unless you subclass */ 227 | typedef struct ev_watcher_time 228 | { 229 | EV_WATCHER_TIME (ev_watcher_time) 230 | } ev_watcher_time; 231 | 232 | /* invoked when fd is either EV_READable or EV_WRITEable */ 233 | /* revent EV_READ, EV_WRITE */ 234 | typedef struct ev_io 235 | { 236 | EV_WATCHER_LIST (ev_io) 237 | 238 | int fd; /* ro */ 239 | int events; /* ro */ 240 | } ev_io; 241 | 242 | /* invoked after a specific time, repeatable (based on monotonic clock) */ 243 | /* revent EV_TIMEOUT */ 244 | typedef struct ev_timer 245 | { 246 | EV_WATCHER_TIME (ev_timer) 247 | 248 | ev_tstamp repeat; /* rw */ 249 | } ev_timer; 250 | 251 | /* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */ 252 | /* revent EV_PERIODIC */ 253 | typedef struct ev_periodic 254 | { 255 | EV_WATCHER_TIME (ev_periodic) 256 | 257 | ev_tstamp offset; /* rw */ 258 | ev_tstamp interval; /* rw */ 259 | ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now); /* rw */ 260 | } ev_periodic; 261 | 262 | /* invoked when the given signal has been received */ 263 | /* revent EV_SIGNAL */ 264 | typedef struct ev_signal 265 | { 266 | EV_WATCHER_LIST (ev_signal) 267 | 268 | int signum; /* ro */ 269 | } ev_signal; 270 | 271 | /* invoked when sigchld is received and waitpid indicates the given pid */ 272 | /* revent EV_CHILD */ 273 | /* does not support priorities */ 274 | typedef struct ev_child 275 | { 276 | EV_WATCHER_LIST (ev_child) 277 | 278 | int flags; /* private */ 279 | int pid; /* ro */ 280 | int rpid; /* rw, holds the received pid */ 281 | int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */ 282 | } ev_child; 283 | 284 | #if EV_STAT_ENABLE 285 | /* st_nlink = 0 means missing file or other error */ 286 | # ifdef _WIN32 287 | typedef struct _stati64 ev_statdata; 288 | # else 289 | typedef struct stat ev_statdata; 290 | # endif 291 | 292 | /* invoked each time the stat data changes for a given path */ 293 | /* revent EV_STAT */ 294 | typedef struct ev_stat 295 | { 296 | EV_WATCHER_LIST (ev_stat) 297 | 298 | ev_timer timer; /* private */ 299 | ev_tstamp interval; /* ro */ 300 | const char *path; /* ro */ 301 | ev_statdata prev; /* ro */ 302 | ev_statdata attr; /* ro */ 303 | 304 | int wd; /* wd for inotify, fd for kqueue */ 305 | } ev_stat; 306 | #endif 307 | 308 | #if EV_IDLE_ENABLE 309 | /* invoked when the nothing else needs to be done, keeps the process from blocking */ 310 | /* revent EV_IDLE */ 311 | typedef struct ev_idle 312 | { 313 | EV_WATCHER (ev_idle) 314 | } ev_idle; 315 | #endif 316 | 317 | /* invoked for each run of the mainloop, just before the blocking call */ 318 | /* you can still change events in any way you like */ 319 | /* revent EV_PREPARE */ 320 | typedef struct ev_prepare 321 | { 322 | EV_WATCHER (ev_prepare) 323 | } ev_prepare; 324 | 325 | /* invoked for each run of the mainloop, just after the blocking call */ 326 | /* revent EV_CHECK */ 327 | typedef struct ev_check 328 | { 329 | EV_WATCHER (ev_check) 330 | } ev_check; 331 | 332 | #if EV_FORK_ENABLE 333 | /* the callback gets invoked before check in the child process when a fork was detected */ 334 | typedef struct ev_fork 335 | { 336 | EV_WATCHER (ev_fork) 337 | } ev_fork; 338 | #endif 339 | 340 | #if EV_EMBED_ENABLE 341 | /* used to embed an event loop inside another */ 342 | /* the callback gets invoked when the event loop has handled events, and can be 0 */ 343 | typedef struct ev_embed 344 | { 345 | EV_WATCHER (ev_embed) 346 | 347 | struct ev_loop *other; /* ro */ 348 | ev_io io; /* private */ 349 | ev_prepare prepare; /* private */ 350 | ev_check check; /* unused */ 351 | ev_timer timer; /* unused */ 352 | ev_periodic periodic; /* unused */ 353 | ev_idle idle; /* unused */ 354 | ev_fork fork; /* private */ 355 | } ev_embed; 356 | #endif 357 | 358 | #if EV_ASYNC_ENABLE 359 | /* invoked when somebody calls ev_async_send on the watcher */ 360 | /* revent EV_ASYNC */ 361 | typedef struct ev_async 362 | { 363 | EV_WATCHER (ev_async) 364 | 365 | EV_ATOMIC_T sent; /* private */ 366 | } ev_async; 367 | 368 | # define ev_async_pending(w) (+(w)->sent) 369 | #endif 370 | 371 | /* the presence of this union forces similar struct layout */ 372 | union ev_any_watcher 373 | { 374 | struct ev_watcher w; 375 | struct ev_watcher_list wl; 376 | 377 | struct ev_io io; 378 | struct ev_timer timer; 379 | struct ev_periodic periodic; 380 | struct ev_signal signal; 381 | struct ev_child child; 382 | #if EV_STAT_ENABLE 383 | struct ev_stat stat; 384 | #endif 385 | #if EV_IDLE_ENABLE 386 | struct ev_idle idle; 387 | #endif 388 | struct ev_prepare prepare; 389 | struct ev_check check; 390 | #if EV_FORK_ENABLE 391 | struct ev_fork fork; 392 | #endif 393 | #if EV_EMBED_ENABLE 394 | struct ev_embed embed; 395 | #endif 396 | #if EV_ASYNC_ENABLE 397 | struct ev_async async; 398 | #endif 399 | }; 400 | 401 | /* bits for ev_default_loop and ev_loop_new */ 402 | /* the default */ 403 | #define EVFLAG_AUTO 0x00000000U /* not quite a mask */ 404 | /* flag bits */ 405 | #define EVFLAG_NOENV 0x01000000U /* do NOT consult environment */ 406 | #define EVFLAG_FORKCHECK 0x02000000U /* check for a fork in each iteration */ 407 | /* debugging/feature disable */ 408 | #define EVFLAG_NOINOTIFY 0x00100000U /* do not attempt to use inotify */ 409 | #define EVFLAG_NOSIGFD 0 /* compatibility to pre-3.9 */ 410 | #define EVFLAG_SIGNALFD 0x00200000U /* attempt to use signalfd */ 411 | /* method bits to be ored together */ 412 | #define EVBACKEND_SELECT 0x00000001U /* about anywhere */ 413 | #define EVBACKEND_POLL 0x00000002U /* !win */ 414 | #define EVBACKEND_EPOLL 0x00000004U /* linux */ 415 | #define EVBACKEND_KQUEUE 0x00000008U /* bsd */ 416 | #define EVBACKEND_DEVPOLL 0x00000010U /* solaris 8 */ /* NYI */ 417 | #define EVBACKEND_PORT 0x00000020U /* solaris 10 */ 418 | #define EVBACKEND_ALL 0x0000003FU 419 | 420 | #if EV_PROTOTYPES 421 | int ev_version_major (void); 422 | int ev_version_minor (void); 423 | 424 | unsigned int ev_supported_backends (void); 425 | unsigned int ev_recommended_backends (void); 426 | unsigned int ev_embeddable_backends (void); 427 | 428 | ev_tstamp ev_time (void); 429 | void ev_sleep (ev_tstamp delay); /* sleep for a while */ 430 | 431 | /* Sets the allocation function to use, works like realloc. 432 | * It is used to allocate and free memory. 433 | * If it returns zero when memory needs to be allocated, the library might abort 434 | * or take some potentially destructive action. 435 | * The default is your system realloc function. 436 | */ 437 | void ev_set_allocator (void *(*cb)(void *ptr, long size)); 438 | 439 | /* set the callback function to call on a 440 | * retryable syscall error 441 | * (such as failed select, poll, epoll_wait) 442 | */ 443 | void ev_set_syserr_cb (void (*cb)(const char *msg)); 444 | 445 | #if EV_MULTIPLICITY 446 | EV_INLINE struct ev_loop * 447 | ev_default_loop_uc (void) 448 | { 449 | extern struct ev_loop *ev_default_loop_ptr; 450 | 451 | return ev_default_loop_ptr; 452 | } 453 | 454 | /* the default loop is the only one that handles signals and child watchers */ 455 | /* you can call this as often as you like */ 456 | EV_INLINE struct ev_loop * 457 | ev_default_loop (unsigned int flags) 458 | { 459 | struct ev_loop *loop = ev_default_loop_uc (); 460 | 461 | if (!loop) 462 | { 463 | extern struct ev_loop *ev_default_loop_init (unsigned int flags); 464 | 465 | loop = ev_default_loop_init (flags); 466 | } 467 | 468 | return loop; 469 | } 470 | 471 | /* create and destroy alternative loops that don't handle signals */ 472 | struct ev_loop *ev_loop_new (unsigned int flags); 473 | void ev_loop_destroy (EV_P); 474 | void ev_loop_fork (EV_P); 475 | 476 | ev_tstamp ev_now (EV_P); /* time w.r.t. timers and the eventloop, updated after each poll */ 477 | 478 | #else 479 | 480 | int ev_default_loop (unsigned int flags); /* returns true when successful */ 481 | 482 | EV_INLINE ev_tstamp 483 | ev_now (void) 484 | { 485 | extern ev_tstamp ev_rt_now; 486 | 487 | return ev_rt_now; 488 | } 489 | #endif /* multiplicity */ 490 | 491 | EV_INLINE int 492 | ev_is_default_loop (EV_P) 493 | { 494 | #if EV_MULTIPLICITY 495 | extern struct ev_loop *ev_default_loop_ptr; 496 | 497 | return !!(EV_A == ev_default_loop_ptr); 498 | #else 499 | return 1; 500 | #endif 501 | } 502 | 503 | void ev_default_destroy (void); /* destroy the default loop */ 504 | /* this needs to be called after fork, to duplicate the default loop */ 505 | /* if you create alternative loops you have to call ev_loop_fork on them */ 506 | /* you can call it in either the parent or the child */ 507 | /* you can actually call it at any time, anywhere :) */ 508 | void ev_default_fork (void); 509 | 510 | unsigned int ev_backend (EV_P); /* backend in use by loop */ 511 | 512 | void ev_now_update (EV_P); /* update event loop time */ 513 | 514 | #if EV_WALK_ENABLE 515 | /* walk (almost) all watchers in the loop of a given type, invoking the */ 516 | /* callback on every such watcher. The callback might stop the watcher, */ 517 | /* but do nothing else with the loop */ 518 | void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)); 519 | #endif 520 | 521 | #endif /* prototypes */ 522 | 523 | #define EVLOOP_NONBLOCK 1 /* do not block/wait */ 524 | #define EVLOOP_ONESHOT 2 /* block *once* only */ 525 | #define EVUNLOOP_CANCEL 0 /* undo unloop */ 526 | #define EVUNLOOP_ONE 1 /* unloop once */ 527 | #define EVUNLOOP_ALL 2 /* unloop all loops */ 528 | 529 | #if EV_PROTOTYPES 530 | void ev_loop (EV_P_ int flags); 531 | void ev_unloop (EV_P_ int how); /* set to 1 to break out of event loop, set to 2 to break out of all event loops */ 532 | 533 | /* 534 | * ref/unref can be used to add or remove a refcount on the mainloop. every watcher 535 | * keeps one reference. if you have a long-running watcher you never unregister that 536 | * should not keep ev_loop from running, unref() after starting, and ref() before stopping. 537 | */ 538 | void ev_ref (EV_P); 539 | void ev_unref (EV_P); 540 | 541 | /* 542 | * convenience function, wait for a single event, without registering an event watcher 543 | * if timeout is < 0, do wait indefinitely 544 | */ 545 | void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg); 546 | 547 | # if EV_MINIMAL < 2 548 | unsigned int ev_loop_count (EV_P); /* number of loop iterations */ 549 | unsigned int ev_loop_depth (EV_P); /* #ev_loop enters - #ev_loop leaves */ 550 | void ev_loop_verify (EV_P); /* abort if loop data corrupted */ 551 | 552 | void ev_set_io_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */ 553 | void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */ 554 | 555 | /* advanced stuff for threading etc. support, see docs */ 556 | void ev_set_userdata (EV_P_ void *data); 557 | void *ev_userdata (EV_P); 558 | void ev_set_invoke_pending_cb (EV_P_ void (*invoke_pending_cb)(EV_P)); 559 | void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P), void (*acquire)(EV_P)); 560 | 561 | unsigned int ev_pending_count (EV_P); /* number of pending events, if any */ 562 | void ev_invoke_pending (EV_P); /* invoke all pending watchers */ 563 | 564 | /* 565 | * stop/start the timer handling. 566 | */ 567 | void ev_suspend (EV_P); 568 | void ev_resume (EV_P); 569 | #endif 570 | 571 | #endif 572 | 573 | /* these may evaluate ev multiple times, and the other arguments at most once */ 574 | /* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */ 575 | #define ev_init(ev,cb_) do { \ 576 | ((ev_watcher *)(void *)(ev))->active = \ 577 | ((ev_watcher *)(void *)(ev))->pending = 0; \ 578 | ev_set_priority ((ev), 0); \ 579 | ev_set_cb ((ev), cb_); \ 580 | } while (0) 581 | 582 | #define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0) 583 | #define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0) 584 | #define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0) 585 | #define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0) 586 | #define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0) 587 | #define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0) 588 | #define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */ 589 | #define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */ 590 | #define ev_check_set(ev) /* nop, yes, this is a serious in-joke */ 591 | #define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0) 592 | #define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */ 593 | #define ev_async_set(ev) do { (ev)->sent = 0; } while (0) 594 | 595 | #define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0) 596 | #define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0) 597 | #define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0) 598 | #define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0) 599 | #define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0) 600 | #define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0) 601 | #define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0) 602 | #define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0) 603 | #define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0) 604 | #define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0) 605 | #define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0) 606 | #define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0) 607 | 608 | #define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */ 609 | #define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */ 610 | 611 | #define ev_cb(ev) (ev)->cb /* rw */ 612 | 613 | #if EV_MINPRI == EV_MAXPRI 614 | # define ev_priority(ev) ((ev), EV_MINPRI) 615 | # define ev_set_priority(ev,pri) ((ev), (pri)) 616 | #else 617 | # define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority)) 618 | # define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri) 619 | #endif 620 | 621 | #define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at) 622 | 623 | #ifndef ev_set_cb 624 | # define ev_set_cb(ev,cb_) ev_cb (ev) = (cb_) 625 | #endif 626 | 627 | /* stopping (enabling, adding) a watcher does nothing if it is already running */ 628 | /* stopping (disabling, deleting) a watcher does nothing unless its already running */ 629 | #if EV_PROTOTYPES 630 | 631 | /* feeds an event into a watcher as if the event actually occured */ 632 | /* accepts any ev_watcher type */ 633 | void ev_feed_event (EV_P_ void *w, int revents); 634 | void ev_feed_fd_event (EV_P_ int fd, int revents); 635 | void ev_feed_signal_event (EV_P_ int signum); 636 | void ev_invoke (EV_P_ void *w, int revents); 637 | int ev_clear_pending (EV_P_ void *w); 638 | 639 | void ev_io_start (EV_P_ ev_io *w); 640 | void ev_io_stop (EV_P_ ev_io *w); 641 | 642 | void ev_timer_start (EV_P_ ev_timer *w); 643 | void ev_timer_stop (EV_P_ ev_timer *w); 644 | /* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */ 645 | void ev_timer_again (EV_P_ ev_timer *w); 646 | /* return remaining time */ 647 | ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w); 648 | 649 | #if EV_PERIODIC_ENABLE 650 | void ev_periodic_start (EV_P_ ev_periodic *w); 651 | void ev_periodic_stop (EV_P_ ev_periodic *w); 652 | void ev_periodic_again (EV_P_ ev_periodic *w); 653 | #endif 654 | 655 | /* only supported in the default loop */ 656 | void ev_signal_start (EV_P_ ev_signal *w); 657 | void ev_signal_stop (EV_P_ ev_signal *w); 658 | 659 | /* only supported in the default loop */ 660 | void ev_child_start (EV_P_ ev_child *w); 661 | void ev_child_stop (EV_P_ ev_child *w); 662 | 663 | # if EV_STAT_ENABLE 664 | void ev_stat_start (EV_P_ ev_stat *w); 665 | void ev_stat_stop (EV_P_ ev_stat *w); 666 | void ev_stat_stat (EV_P_ ev_stat *w); 667 | # endif 668 | 669 | # if EV_IDLE_ENABLE 670 | void ev_idle_start (EV_P_ ev_idle *w); 671 | void ev_idle_stop (EV_P_ ev_idle *w); 672 | # endif 673 | 674 | void ev_prepare_start (EV_P_ ev_prepare *w); 675 | void ev_prepare_stop (EV_P_ ev_prepare *w); 676 | 677 | void ev_check_start (EV_P_ ev_check *w); 678 | void ev_check_stop (EV_P_ ev_check *w); 679 | 680 | # if EV_FORK_ENABLE 681 | void ev_fork_start (EV_P_ ev_fork *w); 682 | void ev_fork_stop (EV_P_ ev_fork *w); 683 | # endif 684 | 685 | # if EV_EMBED_ENABLE 686 | /* only supported when loop to be embedded is in fact embeddable */ 687 | void ev_embed_start (EV_P_ ev_embed *w); 688 | void ev_embed_stop (EV_P_ ev_embed *w); 689 | void ev_embed_sweep (EV_P_ ev_embed *w); 690 | # endif 691 | 692 | # if EV_ASYNC_ENABLE 693 | void ev_async_start (EV_P_ ev_async *w); 694 | void ev_async_stop (EV_P_ ev_async *w); 695 | void ev_async_send (EV_P_ ev_async *w); 696 | # endif 697 | 698 | #endif 699 | 700 | #ifdef __cplusplus 701 | } 702 | #endif 703 | 704 | #endif 705 | 706 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/loop.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOOP_H_ 2 | #define _LOOP_H_ 3 | 4 | #include "bool.h" 5 | #define loop while (true) 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /paired-threaded-ipc/include/rand.h: -------------------------------------------------------------------------------- 1 | inline int 2 | random_get(int max); 3 | 4 | inline int 5 | random_in_range(int min, int max); 6 | 7 | inline int 8 | random_in_range_percent(int base, float percent); 9 | -------------------------------------------------------------------------------- /paired-threaded-ipc/rand-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rand.h" 4 | 5 | const int LOW = 1; 6 | const int HIGH = 6; 7 | 8 | inline int 9 | roll() 10 | { 11 | //return random_in_range(LOW, HIGH); 12 | //Base, Percent 13 | return random_in_range_percent(128, 15); 14 | } 15 | 16 | int main() 17 | { 18 | printf("You roll [%d] [%d]\n", roll(), roll()); 19 | printf("PC rolls [%d] [%d]\n", roll(), roll()); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /paired-threaded-ipc/rand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rand.h" 6 | 7 | inline static void 8 | random_init() 9 | { 10 | struct timeval tp; 11 | gettimeofday(&tp, NULL); 12 | srand((unsigned int) tp.tv_usec + (unsigned int) getpid()); 13 | } 14 | 15 | inline int 16 | random_get(int max) { 17 | return random_in_range(0, max); 18 | } 19 | 20 | inline int 21 | random_in_range(int min, int max) 22 | { 23 | static int initialized = 0; 24 | 25 | if (!initialized) { 26 | random_init(); 27 | initialized = 1; 28 | } 29 | 30 | return rand() % (max - min + 1) + min; 31 | } 32 | 33 | #include 34 | inline int 35 | random_in_range_percent(int base, float percent) 36 | { 37 | float percentage; 38 | int variance; 39 | 40 | percentage = percent / 100.0; 41 | variance = (int) (percentage * base); 42 | variance = random_get(variance * 2) - variance; 43 | 44 | return base + variance; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /reference-implementations-js/tcp.js: -------------------------------------------------------------------------------- 1 | var net = require("net"), 2 | repl = require("repl"); 3 | 4 | connections = 0; 5 | 6 | repl.start("node via stdin> "); 7 | 8 | net.createServer(function (socket) { 9 | connections += 1; 10 | repl.start("node via TCP socket> ", socket); 11 | }).listen(5001); 12 | -------------------------------------------------------------------------------- /reference-implementations-js/udp.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function () { 3 | var dgram = require('dgram'), 4 | message = new Buffer("Some bytes"), 5 | client = dgram.createSocket("udp4"); 6 | 7 | client.send(message, 0, message.length, 3333, "localhost"); 8 | client.close(); 9 | }()); 10 | -------------------------------------------------------------------------------- /reference-implementations-js/unix-socket.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function () { 3 | var net = require("net"), 4 | stream; 5 | 6 | stream = net.createConnection('/tmp/libev-echo.sock'); 7 | stream.on('connect', function () { 8 | stream.write("hello world\n"); 9 | }); 10 | stream.on('data', function (data) { 11 | console.log(data.toString()); 12 | stream.end(); 13 | }); 14 | }()); 15 | -------------------------------------------------------------------------------- /src/array-heap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "array-heap.h" 5 | 6 | int array_init(array* arr, int size) { 7 | arr->data = realloc(NULL, sizeof(void*) * size); 8 | if (0 == (size_t)arr->data) { 9 | return -1; 10 | } 11 | arr->length = size; 12 | arr->index = 0; 13 | return 0; 14 | } 15 | 16 | int array_push(array* arr, void* data) { 17 | ((size_t*)arr->data)[arr->index] = (size_t)data; 18 | arr->index += 1; 19 | if (arr->index >= arr->length) { 20 | if (-1 == array_grow(arr, arr->length * 2)) 21 | { 22 | return -1; 23 | } 24 | } 25 | return arr->index - 1; 26 | } 27 | 28 | int array_grow(array* arr, int size) { 29 | if (size <= arr->length) { 30 | return -1; 31 | } 32 | arr->data = realloc(arr->data, sizeof(void*) * size); 33 | if (-1 == (size_t)arr->data) { 34 | return -1; 35 | } 36 | arr->length = size; 37 | return 0; 38 | } 39 | 40 | void array_free(array* arr, void (*free_element)(void*)) { 41 | int i; 42 | for (i = 0; i < arr->index; i += 1) { 43 | free_element((void*)((size_t*)arr->data)[i]); 44 | } 45 | free(arr->data); 46 | arr->index = -1; 47 | arr->length = 0; 48 | arr->data = NULL; 49 | } 50 | -------------------------------------------------------------------------------- /src/array-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "array_heap.h" 4 | 5 | void free_msg(void* msg); 6 | 7 | int main(int argc, char* argv[]) { 8 | array arr; 9 | char* msg = "Hello World!"; 10 | int i; 11 | printf("pre-init: length %i, index %i\n", arr.length, arr.index); 12 | 13 | array_init(&arr, 32); 14 | printf("post-init: length %i, index %i\n", arr.length, arr.index); 15 | 16 | for (i = 0; i < 64; i += 1) { 17 | array_push(&arr, msg); 18 | } 19 | printf("post-add: length %i, index %i\n", arr.length, arr.index); 20 | 21 | array_free(&arr, free_msg); 22 | printf("post-free: length %i, index %i\n", arr.length, arr.index); 23 | 24 | printf("didn't segfault\n"); 25 | return 0; 26 | } 27 | 28 | void free_msg(void* msg) { 29 | // we don't actually need to free anything 30 | // in this simple example 31 | } 32 | -------------------------------------------------------------------------------- /src/udp-echo-server.c: -------------------------------------------------------------------------------- 1 | // Source: http://www.mail-archive.com/libev@lists.schmorp.de/msg00987.html 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define DEFAULT_PORT 3333 14 | #define BUF_SIZE 4096 15 | 16 | // Lots of globals, what's the best way to get rid of these? 17 | int sd; // socket descriptor 18 | struct sockaddr_in addr; 19 | int addr_len = sizeof(addr); 20 | char buffer[BUF_SIZE]; 21 | 22 | // This callback is called when data is readable on the UDP socket. 23 | static void udp_cb(EV_P_ ev_io *w, int revents) { 24 | puts("udp socket has become readable"); 25 | socklen_t bytes = recvfrom(sd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*) &addr, (socklen_t *) &addr_len); 26 | 27 | // add a null to terminate the input, as we're going to use it as a string 28 | buffer[bytes] = '\0'; 29 | 30 | printf("udp client said: %s", buffer); 31 | 32 | // Echo it back. 33 | // WARNING: this is probably not the right way to do it with libev. 34 | // Question: should we be setting a callback on sd becomming writable here instead? 35 | sendto(sd, buffer, bytes, 0, (struct sockaddr*) &addr, sizeof(addr)); 36 | } 37 | 38 | int main(void) { 39 | int port = DEFAULT_PORT; 40 | puts("udp_echo server started..."); 41 | 42 | // Setup a udp listening socket. 43 | sd = socket(PF_INET, SOCK_DGRAM, 0); 44 | bzero(&addr, sizeof(addr)); 45 | addr.sin_family = AF_INET; 46 | addr.sin_port = htons(port); 47 | addr.sin_addr.s_addr = INADDR_ANY; 48 | if (bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0) 49 | perror("bind"); 50 | 51 | // Do the libev stuff. 52 | struct ev_loop *loop = ev_default_loop(0); 53 | ev_io udp_watcher; 54 | ev_io_init(&udp_watcher, udp_cb, sd, EV_READ); 55 | ev_io_start(loop, &udp_watcher); 56 | ev_loop(loop, 0); 57 | 58 | // This point is never reached. 59 | close(sd); 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /src/unix-echo-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include // fcntl 9 | #include // close 10 | 11 | // TODO 12 | // disconnect and reconnect on each newline 13 | 14 | // Nasty globals for now 15 | // feel free to fork this example and clean it up 16 | EV_P; 17 | ev_io stdin_watcher; 18 | ev_io remote_w; 19 | ev_io send_w; 20 | int remote_fd; 21 | char* line = NULL; 22 | size_t len = 0; 23 | 24 | static void send_cb (EV_P_ ev_io *w, int revents) 25 | { 26 | if (revents & EV_WRITE) 27 | { 28 | puts ("remote ready for writing..."); 29 | 30 | if (-1 == send(remote_fd, line, len, 0)) { 31 | perror("echo send"); 32 | exit(EXIT_FAILURE); 33 | } 34 | // once the data is sent, stop notifications that 35 | // data can be sent until there is actually more 36 | // data to send 37 | ev_io_stop(EV_A_ &send_w); 38 | ev_io_set(&send_w, remote_fd, EV_READ); 39 | ev_io_start(EV_A_ &send_w); 40 | } 41 | else if (revents & EV_READ) 42 | { 43 | int n; 44 | char str[100] = ".\0"; 45 | 46 | printf("[r,remote]"); 47 | n = recv(remote_fd, str, 100, 0); 48 | if (n <= 0) { 49 | if (0 == n) { 50 | // an orderly disconnect 51 | puts("orderly disconnect"); 52 | ev_io_stop(EV_A_ &send_w); 53 | close(remote_fd); 54 | } else if (EAGAIN == errno) { 55 | puts("should never get in this state with libev"); 56 | } else { 57 | perror("recv"); 58 | } 59 | return; 60 | } 61 | printf("socket client said: %s", str); 62 | 63 | } 64 | } 65 | 66 | static void stdin_cb (EV_P_ ev_io *w, int revents) 67 | { 68 | int len2; // not sure if this is at all useful 69 | 70 | puts ("stdin written to, reading..."); 71 | len2 = getline(&line, &len, stdin); 72 | ev_io_stop(EV_A_ &send_w); 73 | ev_io_set (&send_w, remote_fd, EV_READ | EV_WRITE); 74 | ev_io_start(EV_A_ &send_w); 75 | } 76 | 77 | static void remote_cb (EV_P_ ev_io *w, int revents) 78 | { 79 | puts ("connected, now watching stdin"); 80 | // Once the connection is established, listen to stdin 81 | ev_io_start(EV_A_ &stdin_watcher); 82 | // Once we're connected, that's the end of that 83 | ev_io_stop(EV_A_ &remote_w); 84 | } 85 | 86 | 87 | // Simply adds O_NONBLOCK to the file descriptor of choice 88 | int setnonblock(int fd) 89 | { 90 | int flags; 91 | 92 | flags = fcntl(fd, F_GETFL); 93 | flags |= O_NONBLOCK; 94 | return fcntl(fd, F_SETFL, flags); 95 | } 96 | 97 | static void connection_new(EV_P_ char* sock_path) { 98 | int len; 99 | struct sockaddr_un remote; 100 | 101 | if (-1 == (remote_fd = socket(AF_UNIX, SOCK_STREAM, 0))) { 102 | perror("socket"); 103 | exit(1); 104 | } 105 | 106 | // Set it non-blocking 107 | if (-1 == setnonblock(remote_fd)) { 108 | perror("echo client socket nonblock"); 109 | exit(EXIT_FAILURE); 110 | } 111 | 112 | // this should be initialized before the connect() so 113 | // that no packets are dropped when initially sent? 114 | // http://cr.yp.to/docs/connect.html 115 | 116 | // initialize the connect callback so that it starts the stdin asap 117 | ev_io_init (&remote_w, remote_cb, remote_fd, EV_WRITE); 118 | ev_io_start(EV_A_ &remote_w); 119 | // initialize the send callback, but wait to start until there is data to write 120 | ev_io_init(&send_w, send_cb, remote_fd, EV_READ); 121 | ev_io_start(EV_A_ &send_w); 122 | 123 | remote.sun_family = AF_UNIX; 124 | strcpy(remote.sun_path, sock_path); 125 | len = strlen(remote.sun_path) + sizeof(remote.sun_family); 126 | 127 | if (-1 == connect(remote_fd, (struct sockaddr *)&remote, len)) { 128 | perror("connect"); 129 | //exit(1); 130 | } 131 | } 132 | 133 | int main (void) 134 | { 135 | loop = EV_DEFAULT; 136 | // initialise an io watcher, then start it 137 | // this one will watch for stdin to become readable 138 | setnonblock(0); 139 | ev_io_init(&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); 140 | 141 | connection_new(EV_A_ "/tmp/libev-echo.sock"); 142 | 143 | // now wait for events to arrive 144 | ev_loop(EV_A_ 0); 145 | 146 | // unloop was called, so exit 147 | return 0; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /src/unix-echo-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | //#include 13 | #define g_warning printf 14 | 15 | //#include 16 | #include 17 | 18 | #include "array-heap.h" 19 | 20 | struct sock_ev_serv { 21 | ev_io io; 22 | int fd; 23 | struct sockaddr_un socket; 24 | int socket_len; 25 | array clients; 26 | }; 27 | 28 | struct sock_ev_client { 29 | ev_io io; 30 | int fd; 31 | int index; 32 | struct sock_ev_serv* server; 33 | }; 34 | 35 | int setnonblock(int fd); 36 | static void not_blocked(EV_P_ ev_periodic *w, int revents); 37 | 38 | // This callback is called when client data is available 39 | static void client_cb(EV_P_ ev_io *w, int revents) { 40 | // a client has become readable 41 | 42 | struct sock_ev_client* client = (struct sock_ev_client*) w; 43 | 44 | int n; 45 | char str[100] = ".\0"; 46 | 47 | printf("[r]"); 48 | n = recv(client->fd, str, 100, 0); 49 | if (n <= 0) { 50 | if (0 == n) { 51 | // an orderly disconnect 52 | puts("orderly disconnect"); 53 | ev_io_stop(EV_A_ &client->io); 54 | close(client->fd); 55 | } else if (EAGAIN == errno) { 56 | puts("should never get in this state with libev"); 57 | } else { 58 | perror("recv"); 59 | } 60 | return; 61 | } 62 | printf("socket client said: %s", str); 63 | 64 | // Assuming that whenever a client is readable, it is also writable ? 65 | if (send(client->fd, str, n, 0) < 0) { 66 | perror("send"); 67 | } 68 | } 69 | 70 | inline static struct sock_ev_client* client_new(int fd) { 71 | struct sock_ev_client* client; 72 | 73 | client = realloc(NULL, sizeof(struct sock_ev_client)); 74 | client->fd = fd; 75 | //client->server = server; 76 | setnonblock(client->fd); 77 | ev_io_init(&client->io, client_cb, client->fd, EV_READ); 78 | 79 | return client; 80 | } 81 | 82 | // This callback is called when data is readable on the unix socket. 83 | static void server_cb(EV_P_ ev_io *w, int revents) { 84 | puts("unix stream socket has become readable"); 85 | 86 | int client_fd; 87 | struct sock_ev_client* client; 88 | 89 | // since ev_io is the first member, 90 | // watcher `w` has the address of the 91 | // start of the sock_ev_serv struct 92 | struct sock_ev_serv* server = (struct sock_ev_serv*) w; 93 | 94 | while (1) 95 | { 96 | client_fd = accept(server->fd, NULL, NULL); 97 | if( client_fd == -1 ) 98 | { 99 | if( errno != EAGAIN && errno != EWOULDBLOCK ) 100 | { 101 | g_warning("accept() failed errno=%i (%s)", errno, strerror(errno)); 102 | exit(EXIT_FAILURE); 103 | } 104 | break; 105 | } 106 | puts("accepted a client"); 107 | client = client_new(client_fd); 108 | client->server = server; 109 | client->index = array_push(&server->clients, client); 110 | ev_io_start(EV_A_ &client->io); 111 | } 112 | } 113 | 114 | // Simply adds O_NONBLOCK to the file descriptor of choice 115 | int setnonblock(int fd) 116 | { 117 | int flags; 118 | 119 | flags = fcntl(fd, F_GETFL); 120 | flags |= O_NONBLOCK; 121 | return fcntl(fd, F_SETFL, flags); 122 | } 123 | 124 | int unix_socket_init(struct sockaddr_un* socket_un, char* sock_path, int max_queue) { 125 | int fd; 126 | 127 | unlink(sock_path); 128 | 129 | // Setup a unix socket listener. 130 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 131 | if (-1 == fd) { 132 | perror("echo server socket"); 133 | exit(EXIT_FAILURE); 134 | } 135 | 136 | // Set it non-blocking 137 | if (-1 == setnonblock(fd)) { 138 | perror("echo server socket nonblock"); 139 | exit(EXIT_FAILURE); 140 | } 141 | 142 | // Set it as unix socket 143 | socket_un->sun_family = AF_UNIX; 144 | strcpy(socket_un->sun_path, sock_path); 145 | 146 | return fd; 147 | } 148 | 149 | int server_init(struct sock_ev_serv* server, char* sock_path, int max_queue) { 150 | server->fd = unix_socket_init(&server->socket, sock_path, max_queue); 151 | server->socket_len = sizeof(server->socket.sun_family) + strlen(server->socket.sun_path); 152 | 153 | array_init(&server->clients, 128); 154 | 155 | if (-1 == bind(server->fd, (struct sockaddr*) &server->socket, server->socket_len)) 156 | { 157 | perror("echo server bind"); 158 | exit(EXIT_FAILURE); 159 | } 160 | 161 | if (-1 == listen(server->fd, max_queue)) { 162 | perror("listen"); 163 | exit(EXIT_FAILURE); 164 | } 165 | return 0; 166 | } 167 | 168 | int main(void) { 169 | int max_queue = 128; 170 | struct sock_ev_serv server; 171 | struct ev_periodic every_few_seconds; 172 | // Create our single-loop for this single-thread application 173 | EV_P = ev_default_loop(0); 174 | 175 | // Create unix socket in non-blocking fashion 176 | server_init(&server, "/tmp/libev-echo.sock", max_queue); 177 | 178 | // To be sure that we aren't actually blocking 179 | ev_periodic_init(&every_few_seconds, not_blocked, 0, 5, 0); 180 | ev_periodic_start(EV_A_ &every_few_seconds); 181 | 182 | // Get notified whenever the socket is ready to read 183 | ev_io_init(&server.io, server_cb, server.fd, EV_READ); 184 | ev_io_start(EV_A_ &server.io); 185 | 186 | // Run our loop, ostensibly forever 187 | puts("unix-socket-echo starting...\n"); 188 | ev_loop(EV_A_ 0); 189 | 190 | // This point is only ever reached if the loop is manually exited 191 | close(server.fd); 192 | return EXIT_SUCCESS; 193 | } 194 | 195 | 196 | static void not_blocked(EV_P_ ev_periodic *w, int revents) { 197 | puts("I'm not blocked"); 198 | } 199 | --------------------------------------------------------------------------------