iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,244 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef LIB_REACT
8
+ #define LIB_REACT "0.3.0"
9
+ #define LIB_REACT_VERSION_MAJOR 0
10
+ #define LIB_REACT_VERSION_MINOR 3
11
+ #define LIB_REACT_VERSION_PATCH 0
12
+
13
+ #ifndef _GNU_SOURCE
14
+ #define _GNU_SOURCE
15
+ #endif
16
+
17
+ #ifndef REACTOR_MAX_EVENTS
18
+ #define REACTOR_MAX_EVENTS 64
19
+ #endif
20
+ #ifndef REACTOR_TICK
21
+ #define REACTOR_TICK 256 /** in milliseconsd */
22
+ #endif
23
+
24
+ #include <stdint.h>
25
+ #include <unistd.h>
26
+ #include <sys/time.h>
27
+ #include <sys/types.h>
28
+
29
+ #if !defined(__unix__) && !defined(__linux__) && !defined(__APPLE__) && \
30
+ !defined(__CYGWIN__)
31
+ #error This library currently supports only Unix based systems (i.e. Linux and BSD)
32
+ #endif
33
+
34
+ /* until linux supports KQueue, which might not happen... */
35
+ #if !defined(__linux__) && !defined(__CYGWIN__)
36
+ #define reactor_epoll 1
37
+ #define reactor_kqueue 0
38
+ #else
39
+ #define reactor_epoll 0
40
+ #define reactor_kqueue 1
41
+ #endif
42
+
43
+ /* *****************************************************************************
44
+ A simple, predictable UUID for file-descriptors, for collision prevention
45
+
46
+ It's protected against duplicate definition (i.e., when including `libsock.h`)
47
+ */
48
+ #ifndef FD_UUID_TYPE_DEFINED
49
+ #define FD_UUID_TYPE_DEFINED
50
+ /** fduuid_u is used to identify a specific connection, helping to manage file
51
+ * descriptor collisions (when a new connection receives an old connection's
52
+ * file descriptor), especially when the `on_close` event is fired after an
53
+ * `accept` was called and the old file descriptor was already recycled.
54
+ *
55
+ * This requires that sizeof(int) < sizeof(uintptr_t) or sizeof(int)*8 >= 32
56
+ */
57
+ typedef union {
58
+ intptr_t uuid;
59
+ struct {
60
+ int fd : (sizeof(int) < sizeof(intptr_t) ? (sizeof(int) * 8) : 24);
61
+ unsigned counter : (sizeof(int) < sizeof(intptr_t)
62
+ ? ((sizeof(intptr_t) - sizeof(int)) * 8)
63
+ : ((sizeof(intptr_t) * 8) - 24));
64
+ } data;
65
+ } fduuid_u;
66
+
67
+ #define FDUUID_FAIL(uuid) (uuid == -1)
68
+ #define sock_uuid2fd(uuid) ((fduuid_u)(uuid)).data.fd
69
+ #endif
70
+
71
+ /*****************************/ /** \file
72
+ This small library implements a reactor pattern using callbacks.
73
+
74
+ Here are the supported events and their callbacks:
75
+
76
+ - File Descriptor events:
77
+
78
+ - Ready to Read (`reactor_on_data` callback).
79
+
80
+ - Ready to Write (`reactor_on_ready` callback).
81
+
82
+ - Closed (`reactor_on_close` callback).
83
+
84
+ Here's a quick example for an HTTP hello world (no HTTP parsing required)...:
85
+
86
+ #include "libsock.h" // easy socket functions, also allows integration.
87
+ #include "libreact.h" // the reactor library
88
+
89
+ // a global server socket
90
+ int srvfd = -1;
91
+ // a global running flag
92
+ int run = 1;
93
+
94
+ // create the callback. This callback will be global and hardcoded,
95
+ // so there are no runtime function pointer resolutions.
96
+ void reactor_on_data(int fd) {
97
+ if (fd == srvfd) {
98
+ int new_client;
99
+ // accept connections.
100
+ while ((new_client = sock_accept(fd)) > 0) {
101
+ fprintf(stderr, "Accepted new connetion\n");
102
+ reactor_add(new_client);
103
+ }
104
+ fprintf(stderr, "No more clients... (or error)?\n");
105
+ } else {
106
+ fprintf(stderr, "Handling incoming data.\n");
107
+ // handle data
108
+ char data[1024];
109
+ ssize_t len;
110
+ while ((len = sock_read(fd, data, 1024)) > 0)
111
+ sock_write(fd,
112
+ "HTTP/1.1 200 OK\r\n"
113
+ "Content-Length: 12\r\n"
114
+ "Connection: keep-alive\r\n"
115
+ "Keep-Alive: 1;timeout=5\r\n"
116
+ "\r\n"
117
+ "Hello World!",
118
+ 100);
119
+ }
120
+ }
121
+
122
+ void reactor_on_close(int fd) {
123
+ fprintf(stderr, "%d closed the connection.\n", fd);
124
+ }
125
+
126
+ void stop_signal(int sig) {
127
+ run = 0;
128
+ signal(sig, SIG_DFL);
129
+ }
130
+ int main() {
131
+ srvfd = sock_listen(NULL, "3000");
132
+ signal(SIGINT, stop_signal);
133
+ signal(SIGTERM, stop_signal);
134
+ sock_lib_init();
135
+ reactor_init();
136
+ reactor_add(srvfd);
137
+ fprintf(stderr, "Starting reactor loop\n");
138
+ while (run && reactor_review() >= 0)
139
+ ;
140
+ fprintf(stderr, "\nGoodbye.\n");
141
+ }
142
+
143
+ */
144
+
145
+ /* *****************************************************************************
146
+ Initialization and global workflow.
147
+ */
148
+
149
+ /**
150
+ Initializes the processes reactor object.
151
+
152
+ Reactor objects are a per-process object. Avoid forking a process with an active
153
+ reactor, as some unexpected results might occur.
154
+
155
+ Returns -1 on error, otherwise returns 0.
156
+ */
157
+ ssize_t reactor_init();
158
+ /**
159
+ Reviews any pending events (up to REACTOR_MAX_EVENTS) and calls any callbacks.
160
+
161
+ Returns -1 on error, otherwise returns the number of events handled by the
162
+ reactor.
163
+ */
164
+ int reactor_review();
165
+ /**
166
+ Closes the reactor, releasing it's resources.
167
+ */
168
+ void reactor_stop();
169
+
170
+ /* *****************************************************************************
171
+ Adding and removing normal file descriptors.
172
+ */
173
+
174
+ /**
175
+ Adds a file descriptor to the reactor, so that callbacks will be called for it's
176
+ events.
177
+
178
+ Returns -1 on error, otherwise return value is system dependent.
179
+ */
180
+ int reactor_add(intptr_t uuid);
181
+
182
+ /**
183
+ Adds a timer file descriptor, so that callbacks will be called for it's events.
184
+
185
+ Returns -1 on error, otherwise return value is system dependent.
186
+ */
187
+ int reactor_add_timer(intptr_t uuid, long milliseconds);
188
+ /**
189
+ Removes a file descriptor from the reactor - further callbacks for this file
190
+ descriptor won't be called.
191
+
192
+ Returns -1 on error, otherwise return value is system dependent. If the file
193
+ descriptor wasn't owned by the reactor, it isn't an error.
194
+ */
195
+ int reactor_remove(intptr_t uuid);
196
+ /**
197
+ Removes a timer file descriptor from the reactor - further callbacks for this
198
+ file descriptor's timer events won't be called.
199
+
200
+ Returns -1 on error, otherwise return value is system dependent. If the file
201
+ descriptor wasn't owned by the reactor, it isn't an error.
202
+ */
203
+ int reactor_remove_timer(intptr_t uuid);
204
+ /**
205
+ Closes a file descriptor, calling it's callback if it was registered with the
206
+ reactor.
207
+ */
208
+ void reactor_close(intptr_t uuid);
209
+
210
+ /* *****************************************************************************
211
+ Timers.
212
+ */
213
+
214
+ /**
215
+ Adds a file descriptor as a timer object.
216
+
217
+ Returns -1 on error, otherwise return value is system dependent.
218
+ */
219
+ int reactor_add_timer(intptr_t uuid, long milliseconds);
220
+ /**
221
+ epoll requires the timer to be "reset" before repeating. Kqueue requires no such
222
+ thing.
223
+
224
+ This method promises that the timer will be repeated when running on epoll. This
225
+ method is redundent on kqueue.
226
+ */
227
+ void reactor_reset_timer(intptr_t uuid);
228
+ /**
229
+ Creates a timer file descriptor, system dependent.
230
+
231
+ Returns -1 on error, or a valid fd on success (not an fd UUID, as these are
232
+ controlled by `libsock` and `libreact` can be used independently as well).
233
+ */
234
+ intptr_t reactor_make_timer();
235
+
236
+ #if defined(__cplusplus)
237
+ extern "C" {
238
+ #endif
239
+
240
+ #if defined(__cplusplus)
241
+ } /* extern "C" */
242
+ #endif
243
+
244
+ #endif /* end of include guard: LIB_REACT */
@@ -0,0 +1,912 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef _GNU_SOURCE
8
+ #define _GNU_SOURCE
9
+ #endif
10
+ #include "libserver.h"
11
+ #include <string.h>
12
+ #include <signal.h>
13
+ #include <pthread.h>
14
+ #include <sys/mman.h>
15
+ #include <sys/wait.h>
16
+ #include <errno.h>
17
+
18
+ /* *****************************************************************************
19
+ Connection Data
20
+ */
21
+ typedef struct {
22
+ protocol_s* protocol;
23
+ time_t active;
24
+ uint8_t timeout;
25
+ spn_lock_i lock;
26
+ } fd_data_s;
27
+
28
+ /*
29
+ These macros mean we won't need to change code if we change the locking system.
30
+ */
31
+
32
+ #define lock_fd_init(fd) (fd)->lock = SPN_LOCK_INIT
33
+ #define lock_fd_destroy(fd) spn_unlock(&((fd)->lock))
34
+ /** returns 0 on success, value on failure */
35
+ #define try_lock_fd(fd) spn_trylock(&((fd)->lock))
36
+ #define lock_fd(fd) spn_lock(&((fd)->lock))
37
+ #define unlock_fd(fd) spn_unlock(&((fd)->lock))
38
+ #define clear_fd_data(fd_data) \
39
+ { *(fd_data) = (fd_data_s){.lock = (fd_data)->lock}; }
40
+
41
+ /* *****************************************************************************
42
+ Server Core Data
43
+ */
44
+ static struct {
45
+ fd_data_s* fds;
46
+ time_t last_tick;
47
+ void (*on_idle)(void);
48
+ size_t capacity;
49
+ uint8_t running;
50
+ } server_data = {.fds = NULL};
51
+
52
+ /*
53
+ These macros help prevent code changes when changing the data struct.
54
+ */
55
+
56
+ #define valid_uuid(uuid) sock_isvalid(uuid)
57
+
58
+ #define fd_data(fd) server_data.fds[(fd)]
59
+
60
+ #define uuid_data(uuid) fd_data(sock_uuid2fd(uuid))
61
+
62
+ #define clear_uuid(uuid) clear_fd_data(server_data.fds + sock_uuid2fd(uuid))
63
+
64
+ #define protocol_fd(fd) (server_data.fds[(fd)].protocol)
65
+ #define protocol_uuid(uuid) protocol_fd(sock_uuid2fd(uuid))
66
+
67
+ #define fduuid_get(ifd) (server_data.fds[(ifd)].uuid)
68
+
69
+ #define protocol_is_busy(protocol) \
70
+ spn_is_locked(&(((protocol_s*)(protocol))->callback_lock))
71
+ #define protocol_unset_busy(protocol) \
72
+ spn_unlock(&(((protocol_s*)(protocol))->callback_lock))
73
+ #define protocol_set_busy(protocol) \
74
+ spn_trylock(&(((protocol_s*)(protocol))->callback_lock))
75
+
76
+ #define try_lock_uuid(uuid) try_lock_fd(server_data.fds + sock_uuid2fd(uuid))
77
+ #define lock_uuid(uuid) lock_fd(server_data.fds + sock_uuid2fd(uuid))
78
+ #define unlock_uuid(uuid) unlock_fd(server_data.fds + sock_uuid2fd(uuid))
79
+
80
+ // run through any open sockets and call the shutdown handler
81
+ static inline void server_on_shutdown(void) {
82
+ if (server_data.fds && server_data.capacity > 0) {
83
+ for (size_t i = 0; i < server_data.capacity; i++) {
84
+ if (server_data.fds[i].protocol == NULL)
85
+ continue;
86
+ intptr_t uuid = sock_fd2uuid(i);
87
+ if (uuid != -1) {
88
+ if (server_data.fds[i].protocol->on_shutdown != NULL)
89
+ server_data.fds[i].protocol->on_shutdown(uuid,
90
+ server_data.fds[i].protocol);
91
+ sock_close(uuid);
92
+ sock_flush_strong(uuid);
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ static void server_cleanup(void) {
99
+ // run through any open sockets and call the shutdown handler
100
+ server_on_shutdown();
101
+ // free any lock objects (no need to change code if changing locking systems)
102
+ for (size_t i = 0; i < server_data.capacity - 1; i++) {
103
+ server_data.fds[i] = (fd_data_s){0};
104
+ lock_fd_destroy(server_data.fds + i);
105
+ }
106
+ // free memory
107
+ if (server_data.fds) {
108
+ munmap(server_data.fds, sizeof(fd_data_s) * server_data.capacity);
109
+ server_data.fds = NULL;
110
+ }
111
+ }
112
+
113
+ static void init_server(void) {
114
+ pthread_mutex_t inner_lock = PTHREAD_MUTEX_INITIALIZER;
115
+ pthread_mutex_lock(&inner_lock);
116
+ if (server_data.fds == NULL) {
117
+ server_data.capacity = sock_max_capacity();
118
+ atexit(server_cleanup);
119
+ server_data.fds = mmap(NULL, sizeof(fd_data_s) * server_data.capacity,
120
+ PROT_READ | PROT_WRITE | PROT_EXEC,
121
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
122
+ for (size_t i = 0; i < server_data.capacity - 1; i++) {
123
+ server_data.fds[i] = (fd_data_s){0};
124
+ lock_fd_init(server_data.fds + i);
125
+ }
126
+ }
127
+ pthread_mutex_unlock(&inner_lock);
128
+ }
129
+
130
+ /** initializes the library if it wasn't already initialized. */
131
+ #define validate_mem() \
132
+ { \
133
+ if (server_data.fds == NULL) \
134
+ init_server(); \
135
+ }
136
+
137
+ /* *****************************************************************************
138
+ `libsock` Callback Implementation
139
+ */
140
+
141
+ void sock_touch(intptr_t uuid) {
142
+ if (server_data.fds != NULL)
143
+ uuid_data(uuid).active = server_data.last_tick;
144
+ }
145
+
146
+ /* *****************************************************************************
147
+ The Reactor Callback Implementation
148
+ */
149
+
150
+ void reactor_on_close_async(void* _pr) {
151
+ if (protocol_set_busy(_pr) == 0) {
152
+ ((protocol_s*)_pr)->on_close(_pr);
153
+ return;
154
+ }
155
+ async_run(reactor_on_close_async, _pr);
156
+ }
157
+
158
+ void reactor_on_close(intptr_t uuid) {
159
+ if (server_data.fds) {
160
+ // get the currect state
161
+ lock_uuid(uuid);
162
+ protocol_s* protocol = protocol_uuid(uuid);
163
+ // clear state
164
+ clear_uuid(uuid);
165
+ unlock_uuid(uuid);
166
+ // call callback
167
+ if (protocol && protocol->on_close)
168
+ reactor_on_close_async(protocol);
169
+ }
170
+ }
171
+
172
+ void reactor_on_data_async(void* _fduuid) {
173
+ intptr_t fduuid = (intptr_t)_fduuid;
174
+ if (!valid_uuid(fduuid) || protocol_uuid(fduuid) == NULL)
175
+ return;
176
+ // try to lock the socket
177
+ if (try_lock_uuid(fduuid))
178
+ goto no_lock;
179
+ // get current state (protocol might have changed during this time)
180
+ protocol_s* protocol = protocol_uuid(fduuid);
181
+ // review protocol and get use privilage
182
+ if (protocol == NULL || protocol_set_busy(protocol)) {
183
+ // fprintf(stderr, "fduuid is busy %p\n", _fduuid);
184
+ unlock_uuid(fduuid);
185
+ goto no_lock;
186
+ }
187
+ // unlock
188
+ unlock_uuid(fduuid);
189
+ // fire event
190
+ if (protocol && protocol->on_data)
191
+ protocol->on_data(fduuid, protocol);
192
+ // clear the original busy flag
193
+ protocol_unset_busy(protocol);
194
+ return;
195
+ no_lock:
196
+ // fprintf(stderr, "no lock for %p\n", _fduuid);
197
+ // failed to aquire lock / busy
198
+ async_run(reactor_on_data_async, _fduuid);
199
+ }
200
+
201
+ void reactor_on_data(intptr_t fd) {
202
+ async_run(reactor_on_data_async, (void*)fd);
203
+ }
204
+
205
+ void reactor_on_ready(intptr_t uuid) {
206
+ uuid_data(uuid).active = server_data.last_tick;
207
+ lock_uuid(uuid);
208
+ protocol_s* protocol = protocol_uuid(uuid);
209
+ unlock_uuid(uuid);
210
+ if (protocol && protocol->on_ready)
211
+ protocol->on_ready(uuid, protocol);
212
+ }
213
+
214
+ /* *****************************************************************************
215
+ Zombie Reaping
216
+ With thanks to Dr Graham D Shaw.
217
+ http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html
218
+ */
219
+
220
+ void reap_child_handler(int sig) {
221
+ int old_errno = errno;
222
+ while (waitpid(-1, NULL, WNOHANG) > 0)
223
+ ;
224
+ errno = old_errno;
225
+ }
226
+
227
+ inline static void reap_children(void) {
228
+ struct sigaction sa;
229
+ sa.sa_handler = reap_child_handler;
230
+ sigemptyset(&sa.sa_mask);
231
+ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
232
+ if (sigaction(SIGCHLD, &sa, 0) == -1) {
233
+ perror("Child reaping initialization failed");
234
+ exit(1);
235
+ }
236
+ }
237
+
238
+ /* *****************************************************************************
239
+ Exit Signal Handling
240
+ */
241
+
242
+ static void stop_server_handler(int sig) {
243
+ server_data.running = 0;
244
+ async_signal();
245
+ #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
246
+ fprintf(stderr, " --- Stop signal received ---\n");
247
+ #endif
248
+ signal(sig, SIG_DFL);
249
+ }
250
+
251
+ inline static void listen_for_stop_signal(void) {
252
+ struct sigaction sa;
253
+ sa.sa_handler = stop_server_handler;
254
+ sigemptyset(&sa.sa_mask);
255
+ sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
256
+ if (sigaction(SIGINT, &sa, 0) || sigaction(SIGTERM, &sa, 0)) {
257
+ perror("Signal registration failed");
258
+ exit(2);
259
+ }
260
+ }
261
+
262
+ /* *****************************************************************************
263
+ The Listenning Protocol
264
+ */
265
+
266
+ static const char* listener_protocol_name = "listening protocol __internal__";
267
+
268
+ struct ListenerProtocol {
269
+ protocol_s protocol;
270
+ protocol_s* (*on_open)(intptr_t uuid, void* udata);
271
+ void* udata;
272
+ void (*on_start)(void* udata);
273
+ void (*on_finish)(void* udata);
274
+ };
275
+
276
+ static void listener_on_data(intptr_t uuid, protocol_s* _listener) {
277
+ intptr_t new_client;
278
+ struct ListenerProtocol* listener = (void*)_listener;
279
+ while ((new_client = sock_accept(uuid)) != -1) {
280
+ // make sure it's a clean slate... although it should be assumed to be.
281
+ lock_uuid(new_client);
282
+ clear_uuid(new_client);
283
+ unlock_uuid(new_client);
284
+ // assume that sock_accept calls reactor_on_close if needed
285
+ protocol_uuid(new_client) = listener->on_open(new_client, listener->udata);
286
+ if (protocol_uuid(new_client)) {
287
+ uuid_data(new_client).active = server_data.last_tick;
288
+ protocol_unset_busy(protocol_uuid(new_client));
289
+ reactor_add(new_client);
290
+ continue;
291
+ } else {
292
+ sock_close(new_client);
293
+ }
294
+ }
295
+ }
296
+
297
+ static void free_listenner(void* _li) {
298
+ free(_li);
299
+ }
300
+
301
+ static void listener_on_close(protocol_s* _listener) {
302
+ if (((struct ListenerProtocol*)_listener)->on_finish)
303
+ ((struct ListenerProtocol*)_listener)
304
+ ->on_finish(((struct ListenerProtocol*)_listener)->udata);
305
+ free_listenner(_listener);
306
+ }
307
+
308
+ static inline struct ListenerProtocol* listener_alloc(
309
+ struct ServerServiceSettings settings) {
310
+ struct ListenerProtocol* listener = malloc(sizeof(*listener));
311
+ if (listener) {
312
+ *listener = (struct ListenerProtocol){
313
+ .protocol.service = listener_protocol_name,
314
+ .protocol.on_data = listener_on_data,
315
+ .protocol.on_close = listener_on_close,
316
+ .on_open = settings.on_open,
317
+ .udata = settings.udata,
318
+ .on_start = settings.on_start,
319
+ .on_finish = settings.on_finish,
320
+ };
321
+ return listener;
322
+ }
323
+ return NULL;
324
+ }
325
+
326
+ inline static void listener_on_server_start(void) {
327
+ for (size_t i = 0; i < server_data.capacity; i++) {
328
+ if (protocol_fd(i) && protocol_fd(i)->service == listener_protocol_name) {
329
+ if (reactor_add(sock_fd2uuid(i)))
330
+ perror("Couldn't register listenning socket"), exit(4);
331
+ // call the on_init callback
332
+ if (((struct ListenerProtocol*)protocol_fd(i))->on_start)
333
+ ((struct ListenerProtocol*)protocol_fd(i))
334
+ ->on_start(((struct ListenerProtocol*)protocol_fd(i))->udata);
335
+ }
336
+ }
337
+ }
338
+ inline static void listener_on_server_shutdown(void) {
339
+ for (size_t i = 0; i < server_data.capacity; i++) {
340
+ if (protocol_fd(i) && protocol_fd(i)->service == listener_protocol_name) {
341
+ sock_close(sock_fd2uuid(i));
342
+ }
343
+ }
344
+ }
345
+
346
+ /* *****************************************************************************
347
+ * The timer protocol
348
+ */
349
+
350
+ /* *******
351
+ Timer Protocol
352
+ ******* */
353
+ typedef struct {
354
+ protocol_s protocol;
355
+ size_t milliseconds;
356
+ size_t repetitions;
357
+ void (*task)(void*);
358
+ void (*on_finish)(void*);
359
+ void* arg;
360
+ } timer_protocol_s;
361
+
362
+ #define prot2timer(protocol) (*((timer_protocol_s*)(protocol)))
363
+
364
+ const char* timer_protocol_name = "timer protocol __internal__";
365
+
366
+ static void timer_on_data(intptr_t uuid, protocol_s* protocol) {
367
+ prot2timer(protocol).task(prot2timer(protocol).arg);
368
+ if (prot2timer(protocol).repetitions) {
369
+ prot2timer(protocol).repetitions -= 1;
370
+ if (prot2timer(protocol).repetitions == 0) {
371
+ // fprintf(stderr, "closing timer?\n");
372
+ reactor_remove_timer(uuid);
373
+ sock_force_close(uuid);
374
+ }
375
+ }
376
+ reactor_reset_timer(uuid);
377
+ }
378
+
379
+ static void timer_on_close(protocol_s* protocol) {
380
+ // fprintf(stderr, "timer closed\n");
381
+ if (prot2timer(protocol).on_finish)
382
+ prot2timer(protocol).on_finish(prot2timer(protocol).arg);
383
+ free(protocol);
384
+ }
385
+
386
+ static inline timer_protocol_s* timer_alloc(void (*task)(void*),
387
+ void* arg,
388
+ size_t milliseconds,
389
+ size_t repetitions,
390
+ void (*on_finish)(void*)) {
391
+ timer_protocol_s* t = malloc(sizeof(*t));
392
+ if (t)
393
+ *t = (timer_protocol_s){
394
+ .protocol.service = timer_protocol_name,
395
+ .protocol.on_data = timer_on_data,
396
+ .protocol.on_close = timer_on_close,
397
+ .arg = arg,
398
+ .task = task,
399
+ .on_finish = on_finish,
400
+ .milliseconds = milliseconds,
401
+ .repetitions = repetitions,
402
+ };
403
+ return t;
404
+ }
405
+
406
+ inline static void timer_on_server_start(void) {
407
+ for (size_t i = 0; i < server_data.capacity; i++) {
408
+ if (protocol_fd(i) && protocol_fd(i)->service == timer_protocol_name) {
409
+ if (reactor_add_timer(sock_fd2uuid(i),
410
+ prot2timer(protocol_fd(i)).milliseconds))
411
+ perror("Couldn't register a required timed event."), exit(4);
412
+ }
413
+ }
414
+ }
415
+
416
+ /* *****************************************************************************
417
+ Reactor cycling and timeout handling
418
+ */
419
+
420
+ static inline void timeout_review(void) {
421
+ static time_t review = 0;
422
+ if (review >= server_data.last_tick)
423
+ return;
424
+ time(&review);
425
+ for (size_t i = 0; i < server_data.capacity; i++) {
426
+ if (protocol_fd(i) == NULL)
427
+ continue; // Protocol objects are required for open connections.
428
+ if (fd_data(i).timeout == 0) {
429
+ if (protocol_fd(i) && protocol_fd(i)->service != listener_protocol_name &&
430
+ protocol_fd(i)->service != timer_protocol_name &&
431
+ review - fd_data(i).active > 300) {
432
+ sock_close(sock_fd2uuid(i));
433
+ }
434
+ continue;
435
+ }
436
+ if (fd_data(i).active + fd_data(i).timeout < review) {
437
+ if (protocol_fd(i)->ping) {
438
+ protocol_fd(i)->ping(sock_fd2uuid(i), protocol_fd(i));
439
+ } else if (!protocol_is_busy(protocol_fd(i)) ||
440
+ (review - fd_data(i).active > 300)) {
441
+ sock_close(sock_fd2uuid(i));
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ static void server_cycle(void* _) {
448
+ static int8_t perform_idle = 1;
449
+ time(&server_data.last_tick);
450
+ if (server_data.running) {
451
+ timeout_review();
452
+ int e_count = reactor_review();
453
+ if (e_count < 0) {
454
+ return;
455
+ }
456
+ if (e_count == 0) {
457
+ if (perform_idle && server_data.on_idle)
458
+ server_data.on_idle();
459
+ perform_idle = 0;
460
+ } else {
461
+ perform_idle = 1;
462
+ }
463
+ async_run(server_cycle, NULL);
464
+ }
465
+ }
466
+ /* *****************************************************************************
467
+ * The Server API
468
+ * (and helper functions)
469
+ */
470
+
471
+ /* *****************************************************************************
472
+ * Server actions
473
+ */
474
+
475
+ #undef server_listen
476
+ #undef server_run
477
+ /**
478
+ Listens to a server with the following server settings (which MUST include
479
+ a default protocol).
480
+
481
+ This method blocks the current thread until the server is stopped (either
482
+ though a `srv_stop` function or when a SIGINT/SIGTERM is received).
483
+ */
484
+ int server_listen(struct ServerServiceSettings settings) {
485
+ validate_mem();
486
+ if (settings.on_open == NULL || settings.port == NULL)
487
+ return -1;
488
+ intptr_t fduuid = sock_listen(settings.address, settings.port);
489
+ if (fduuid == -1)
490
+ return -1;
491
+ server_data.fds[sock_uuid2fd(fduuid)].protocol =
492
+ (void*)listener_alloc(settings);
493
+ if (server_data.fds[sock_uuid2fd(fduuid)].protocol == NULL)
494
+ goto error;
495
+ if (server_data.running && reactor_add(fduuid))
496
+ goto error;
497
+ #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
498
+ fprintf(stderr, "* Listenning on port %s\n", settings.port);
499
+ #endif
500
+ return 0;
501
+ error:
502
+ sock_close(fduuid);
503
+ return -1;
504
+ }
505
+ /** runs the server, hanging the current process and thread. */
506
+ ssize_t server_run(struct ServerSettings settings) {
507
+ validate_mem();
508
+ if (server_data.running) {
509
+ return -1;
510
+ }
511
+ reap_children();
512
+ listen_for_stop_signal();
513
+ server_data.running = 1;
514
+ server_data.on_idle = settings.on_idle;
515
+ if (settings.processes == 0)
516
+ settings.processes = 1;
517
+
518
+ #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
519
+ if (settings.threads == 0)
520
+ fprintf(stderr,
521
+ "* Running %lu processes"
522
+ " in single thread mode.\n",
523
+ settings.processes);
524
+ else
525
+ fprintf(stderr,
526
+ "* Running %lu processes"
527
+ " X %lu threads.\n",
528
+ settings.processes, settings.threads);
529
+ #endif
530
+
531
+ pid_t rootpid = getpid();
532
+ pid_t* children = NULL;
533
+ if (settings.processes > 1) {
534
+ children = malloc(sizeof(*children) * settings.processes);
535
+ for (size_t i = 0; i < settings.processes - 1; i++) {
536
+ if (fork() == 0)
537
+ break;
538
+ }
539
+ }
540
+ if (reactor_init() < 0)
541
+ perror("Reactor initialization failed"), exit(3);
542
+ listener_on_server_start();
543
+ timer_on_server_start();
544
+ #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
545
+ fprintf(stderr, "* [%d] Running.\n", getpid());
546
+ #endif
547
+ async_start(settings.threads);
548
+ if (settings.on_init)
549
+ settings.on_init();
550
+ async_run(server_cycle, NULL);
551
+ if (settings.threads > 0)
552
+ async_join();
553
+ else
554
+ async_perform();
555
+ listener_on_server_shutdown();
556
+ reactor_review();
557
+ server_on_shutdown();
558
+ if (settings.on_finish)
559
+ settings.on_finish();
560
+
561
+ if (children) {
562
+ if (rootpid == getpid()) {
563
+ while (waitpid(-1, NULL, 0) >= 0)
564
+ ;
565
+ }
566
+ free(children);
567
+ }
568
+ #if defined(SERVER_PRINT_STATE) && SERVER_PRINT_STATE == 1
569
+ fprintf(stderr, "* [%d] Shutdown.\n", getpid());
570
+ if (rootpid == getpid())
571
+ fprintf(stderr, "* Shutdown process complete.\n");
572
+ #endif
573
+ if (rootpid != getpid())
574
+ exit(0);
575
+
576
+ return 0;
577
+ }
578
+
579
+ void server_stop(void) {
580
+ server_data.running = 0;
581
+ }
582
+ /**
583
+ Returns the last time the server reviewed any pending IO events.
584
+ */
585
+ time_t server_last_tick(void) {
586
+ return server_data.last_tick;
587
+ }
588
+
589
+ /* *****************************************************************************
590
+ * Socket actions
591
+ */
592
+
593
+ /**
594
+ Sets a new active protocol object for the requested file descriptor.
595
+
596
+ This also schedules the old protocol's `on_close` callback to run, making sure
597
+ all resources are released.
598
+
599
+ Returns -1 on error (i.e. connection closed), otherwise returns 0.
600
+ */
601
+ ssize_t server_switch_protocol(intptr_t fd, protocol_s* new_protocol) {
602
+ if (new_protocol == NULL || valid_uuid(fd) == 0)
603
+ return -1;
604
+ protocol_s* old_protocol;
605
+ lock_uuid(fd);
606
+ old_protocol = uuid_data(fd).protocol;
607
+ uuid_data(fd).protocol = new_protocol;
608
+ unlock_uuid(fd);
609
+ if (old_protocol && old_protocol->on_close)
610
+ reactor_on_close_async(old_protocol);
611
+ return 0;
612
+ }
613
+ /**
614
+ Gets the active protocol object for the requested file descriptor.
615
+
616
+ Returns NULL on error (i.e. connection closed), otherwise returns a `protocol_s`
617
+ pointer.
618
+ */
619
+ protocol_s* server_get_protocol(intptr_t uuid) {
620
+ if (valid_uuid(uuid) == 0)
621
+ return NULL;
622
+ protocol_s* protocol;
623
+ lock_uuid(uuid);
624
+ protocol = uuid_data(uuid).protocol;
625
+ unlock_uuid(uuid);
626
+ return protocol;
627
+ }
628
+ /**
629
+ Sets a connection's timeout.
630
+
631
+ Returns -1 on error (i.e. connection closed), otherwise returns 0.
632
+ */
633
+ void server_set_timeout(intptr_t fd, uint8_t timeout) {
634
+ if (valid_uuid(fd) == 0)
635
+ return;
636
+ lock_uuid(fd);
637
+ uuid_data(fd).timeout = timeout;
638
+ unlock_uuid(fd);
639
+ }
640
+
641
+ /** Attaches an existing connection (fd) to the server's reactor and protocol
642
+ management system, so that the server can be used also to manage connection
643
+ based resources asynchronously (i.e. database resources etc').
644
+
645
+ On failure the fduuid_u.data.fd value will be -1.
646
+ */
647
+ intptr_t server_attach(int fd, protocol_s* protocol) {
648
+ intptr_t uuid = sock_open(fd);
649
+ if (uuid == -1)
650
+ return -1;
651
+ protocol_fd(fd) = protocol;
652
+ if (reactor_add(uuid)) {
653
+ sock_close(uuid);
654
+ return -1;
655
+ }
656
+ return uuid;
657
+ }
658
+ /** Hijack a socket (file descriptor) from the server, clearing up it's
659
+ resources. The control of hte socket is totally relinquished.
660
+
661
+ This method will block until all the data in the buffer is sent before
662
+ releasing control of the socket.
663
+
664
+ The returned value is the fd for the socket, or -1 on error.
665
+ */
666
+ int server_hijack(intptr_t uuid) {
667
+ if (sock_isvalid(uuid) == 0)
668
+ return -1;
669
+ reactor_remove(uuid);
670
+ sock_flush_strong(uuid);
671
+ if (sock_isvalid(uuid) == 0)
672
+ return -1;
673
+ protocol_s* old_protocol;
674
+ lock_uuid(uuid);
675
+ old_protocol = uuid_data(uuid).protocol;
676
+ uuid_data(uuid).protocol = NULL;
677
+ unlock_uuid(uuid);
678
+ if (old_protocol && old_protocol->on_close)
679
+ reactor_on_close_async(old_protocol);
680
+ return sock_uuid2fd(uuid);
681
+ }
682
+ /** Counts the number of connections for the specified protocol (NULL = all
683
+ protocols). */
684
+ long server_count(char* service) {
685
+ long count = 0;
686
+ if (service == NULL) {
687
+ for (size_t i = 0; i < server_data.capacity; i++) {
688
+ if (protocol_fd(i) && protocol_fd(i)->service != listener_protocol_name &&
689
+ protocol_fd(i)->service != timer_protocol_name)
690
+ count++;
691
+ }
692
+ } else {
693
+ for (size_t i = 0; i < server_data.capacity; i++) {
694
+ if (protocol_fd(i) && protocol_fd(i)->service == service)
695
+ count++;
696
+ }
697
+ }
698
+ return count;
699
+ }
700
+
701
+ /* *****************************************************************************
702
+ * Connection Tasks (each and deffer tactics implementations)
703
+ */
704
+
705
+ /* *******
706
+ Task core data
707
+ ******* */
708
+ typedef struct {
709
+ intptr_t origin;
710
+ intptr_t target;
711
+ const char* service;
712
+ void (*task)(intptr_t fd, protocol_s* protocol, void* arg);
713
+ void* on_finish;
714
+ void* arg;
715
+ } srv_task_s;
716
+
717
+ /* Get task from void pointer. */
718
+ #define p2task(task) (*((srv_task_s*)(task)))
719
+
720
+ /* Get fallback callback from the task object. */
721
+ #define task2fallback(task) \
722
+ ((void (*)(intptr_t, void*))(p2task(task).on_finish))
723
+
724
+ /* Get on_finished callback from the task object. */
725
+ #define task2on_done(task) \
726
+ ((void (*)(intptr_t, protocol_s*, void*))(p2task(task).on_finish))
727
+ /* allows for later implementation of a task pool with minimal code updates. */
728
+ static inline srv_task_s* task_alloc(void) {
729
+ return malloc(sizeof(srv_task_s));
730
+ }
731
+
732
+ /* allows for later implementation of a task pool with minimal code updates. */
733
+ static inline void task_free(srv_task_s* task) {
734
+ return free(task);
735
+ }
736
+
737
+ /* performs a single connection task. */
738
+ static void perform_single_task(void* task) {
739
+ if (sock_isvalid(p2task(task).target) == 0) {
740
+ if (p2task(task).on_finish) // an invalid connection fallback
741
+ task2fallback(task)(p2task(task).origin, p2task(task).arg);
742
+ task_free(task);
743
+ return;
744
+ }
745
+ if (try_lock_uuid(p2task(task).target) == 0) {
746
+ // get protocol
747
+ protocol_s* protocol = protocol_uuid(p2task(task).target);
748
+ if (protocol_set_busy(protocol) == 0) {
749
+ // clear the original busy flag
750
+ unlock_uuid(p2task(task).target);
751
+ p2task(task).task(p2task(task).target, protocol, p2task(task).arg);
752
+ protocol_unset_busy(protocol);
753
+ task_free(task);
754
+ return;
755
+ }
756
+ unlock_uuid(p2task(task).target);
757
+ }
758
+ async_run(perform_single_task, task);
759
+ }
760
+
761
+ /* performs a connection group task. */
762
+ static void perform_each_task(void* task) {
763
+ intptr_t uuid;
764
+ protocol_s* protocol;
765
+ while (p2task(task).target < server_data.capacity) {
766
+ uuid = sock_fd2uuid(p2task(task).target);
767
+ if (uuid == -1 || uuid == p2task(task).origin) {
768
+ ++p2task(task).target;
769
+ continue;
770
+ }
771
+ if (try_lock_uuid(uuid) == 0) {
772
+ protocol = protocol_uuid(uuid);
773
+ if (protocol == NULL || protocol->service != p2task(task).service) {
774
+ unlock_uuid(uuid);
775
+ ++p2task(task).target;
776
+ continue;
777
+ } else if (protocol_set_busy(protocol) == 0) {
778
+ // unlock uuid
779
+ unlock_uuid(uuid);
780
+ // perform task
781
+ p2task(task).task(uuid, protocol, p2task(task).arg);
782
+ // clear the busy flag
783
+ protocol_unset_busy(protocol);
784
+ // step forward
785
+ ++p2task(task).target;
786
+ continue;
787
+ }
788
+ // it's the right protocol and service, but we couldn't lock the protocol
789
+ unlock_uuid(uuid);
790
+ }
791
+ async_run(perform_each_task, task);
792
+ return;
793
+ }
794
+ if (p2task(task).on_finish) { // finished group task callback
795
+ task2on_done(task)(
796
+ p2task(task).origin,
797
+ (sock_isvalid(p2task(task).origin) ? protocol_uuid(p2task(task).origin)
798
+ : NULL),
799
+ p2task(task).arg);
800
+ }
801
+ task_free(task);
802
+ return;
803
+ }
804
+
805
+ /* *******
806
+ API
807
+ ******* */
808
+
809
+ /**
810
+ Schedules a specific task to run asyncronously for each connection (except the
811
+ origin connection) on a specific protocol.
812
+ */
813
+ void server_each(intptr_t origin_fd,
814
+ const char* service,
815
+ void (*task)(intptr_t fd, protocol_s* protocol, void* arg),
816
+ void* arg,
817
+ void (*on_finish)(intptr_t fd,
818
+ protocol_s* protocol,
819
+ void* arg)) {
820
+ srv_task_s* t = NULL;
821
+ if (service == NULL || task == NULL)
822
+ goto error;
823
+ t = task_alloc();
824
+ if (t == NULL)
825
+ goto error;
826
+ *t = (srv_task_s){.service = service,
827
+ .origin = origin_fd,
828
+ .task = task,
829
+ .on_finish = on_finish,
830
+ .arg = arg};
831
+ if (async_run(perform_each_task, t))
832
+ goto error;
833
+ return;
834
+ error:
835
+ if (t)
836
+ task_free(t);
837
+ if (on_finish)
838
+ on_finish(origin_fd,
839
+ (sock_isvalid(origin_fd) ? protocol_uuid(origin_fd) : NULL), arg);
840
+ }
841
+
842
+ /** Schedules a specific task to run asyncronously for a specific connection.
843
+ */
844
+ void server_task(intptr_t caller_fd,
845
+ void (*task)(intptr_t fd, protocol_s* protocol, void* arg),
846
+ void* arg,
847
+ void (*fallback)(intptr_t fd, void* arg)) {
848
+ srv_task_s* t = NULL;
849
+ if (task == NULL)
850
+ goto error;
851
+ t = task_alloc();
852
+ if (t == NULL)
853
+ goto error;
854
+ *t = (srv_task_s){
855
+ .target = caller_fd, .task = task, .on_finish = fallback, .arg = arg};
856
+ if (async_run(perform_single_task, t))
857
+ goto error;
858
+ return;
859
+ error:
860
+ if (t)
861
+ task_free(t);
862
+ if (fallback)
863
+ fallback(caller_fd, arg);
864
+ }
865
+
866
+ /* *****************************************************************************
867
+ * Timed tasks
868
+ */
869
+
870
+ /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
871
+ timer to the reactor. The task will repeat `repetitions` times. if
872
+ `repetitions` is set to 0, task will repeat forever. Returns -1 on error
873
+ or the new file descriptor on succeess.
874
+ */
875
+ int server_run_every(size_t milliseconds,
876
+ size_t repetitions,
877
+ void (*task)(void*),
878
+ void* arg,
879
+ void (*on_finish)(void*)) {
880
+ validate_mem();
881
+ if (task == NULL)
882
+ return -1;
883
+ timer_protocol_s* protocol = NULL;
884
+ intptr_t uuid = -1;
885
+ int fd = reactor_make_timer();
886
+ if (fd == -1) {
887
+ // perror("couldn't create a timer fd");
888
+ goto error;
889
+ }
890
+ uuid = sock_open(fd);
891
+ if (uuid == -1)
892
+ goto error;
893
+ clear_uuid(uuid);
894
+ protocol = timer_alloc(task, arg, milliseconds, repetitions, on_finish);
895
+ if (protocol == NULL)
896
+ goto error;
897
+ protocol_fd(fd) = (protocol_s*)protocol;
898
+ if (server_data.running && reactor_add_timer(uuid, milliseconds))
899
+ goto error;
900
+ return 0;
901
+ error:
902
+ if (uuid != -1)
903
+ sock_close(uuid);
904
+ else if (fd != -1)
905
+ close(fd);
906
+
907
+ if (protocol != NULL) {
908
+ protocol_fd(fd) = NULL;
909
+ timer_on_close((protocol_s*)protocol);
910
+ }
911
+ return -1;
912
+ }