├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── example ├── echo.c └── timer.c ├── package.json └── src ├── ae.c ├── ae.h ├── ae_epoll.c ├── ae_evport.c ├── ae_kqueue.c ├── ae_select.c ├── anet.c ├── anet.h ├── config.h ├── fmacros.h └── zmalloc.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | timer 19 | echo 20 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2015, Salvatore Sanfilippo 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC = src/ae.c src/anet.c 2 | OBJ = ${SRC:.c=.o} 3 | CFLAGS = -Wno-parentheses -Wno-switch-enum -Wno-unused-value 4 | 5 | libae.a: $(OBJ) 6 | $(AR) -rc $@ $(OBJ) 7 | 8 | %.o: %.c 9 | $(CC) -c $(CFLAGS) $< -o $@ 10 | 11 | timer: example/timer.o libae.a 12 | $(CC) $^ -o $@ 13 | 14 | echo: example/echo.o libae.a 15 | $(CC) $^ -o $@ 16 | 17 | clean: 18 | rm -f $(OBJ) libae.a example/timer.o timer example/echo.o echo 19 | 20 | .PHONY: clean 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libae 2 | 3 | Redis's async event library, you can use it in your projects. 4 | 5 | ## supported event multiplexing model 6 | 7 | * `epoll` 8 | * `kqueue` 9 | * `ev_port` 10 | * `select` 11 | 12 | ## Example 13 | 14 | ### Timer 15 | 16 | Print `Hello, World` on screen every 10 seconds: 17 | 18 | ```C 19 | int print(struct aeEventLoop *loop, long long id, void *clientData) 20 | { 21 | printf("%lld - Hello, World\n", id); 22 | return -1; 23 | } 24 | 25 | int main(void) 26 | { 27 | aeEventLoop *loop = aeCreateEventLoop(0); 28 | int i; 29 | for (i = 0; i < 10; i ++) { 30 | aeCreateTimeEvent(loop, i*1000, print, NULL, NULL); 31 | } 32 | aeMain(loop); 33 | aeDeleteEventLoop(loop); 34 | return 0; 35 | } 36 | ``` 37 | 38 | ### Echo server 39 | 40 | Start an echo server on 8000: 41 | 42 | ```C 43 | void writeToClient(aeEventLoop *loop, int fd, void *clientdata, int mask) 44 | { 45 | char *buffer = clientdata; 46 | printf("%p\n", clientdata); 47 | write(fd, buffer, strlen(buffer)); 48 | free(buffer); 49 | aeDeleteFileEvent(loop, fd, AE_WRITABLE); 50 | } 51 | 52 | void readFromClient(aeEventLoop *loop, int fd, void *clientdata, int mask) 53 | { 54 | int buffer_size = 1024; 55 | char *buffer = malloc(sizeof(char) * buffer_size); 56 | bzero(buffer, buffer_size); 57 | int size; 58 | size = read(fd, buffer, buffer_size); 59 | aeCreateFileEvent(loop, fd, AE_WRITABLE, writeToClient, buffer); 60 | } 61 | 62 | void acceptTcpHandler(aeEventLoop *loop, int fd, void *clientdata, int mask) 63 | { 64 | int client_port, client_fd; 65 | char client_ip[128]; 66 | // create client socket 67 | client_fd = anetTcpAccept(NULL, fd, client_ip, 128, &client_port); 68 | printf("Accepted %s:%d\n", client_ip, client_port); 69 | 70 | // set client socket non-block 71 | anetNonBlock(NULL, client_fd); 72 | 73 | // regist on message callback 74 | int ret; 75 | ret = aeCreateFileEvent(loop, client_fd, AE_READABLE, readFromClient, NULL); 76 | assert(ret != AE_ERR); 77 | } 78 | 79 | int main() 80 | { 81 | int ipfd; 82 | // create server socket 83 | ipfd = anetTcpServer(NULL, 8000, "0.0.0.0", 0); 84 | assert(ipfd != ANET_ERR); 85 | 86 | // create main event loop 87 | aeEventLoop *loop; 88 | loop = aeCreateEventLoop(1024); 89 | 90 | // regist socket connect callback 91 | int ret; 92 | ret = aeCreateFileEvent(loop, ipfd, AE_READABLE, acceptTcpHandler, NULL); 93 | assert(ret != AE_ERR); 94 | 95 | // start main loop 96 | aeMain(loop); 97 | 98 | // stop loop 99 | aeDeleteEventLoop(loop); 100 | 101 | return 0; 102 | } 103 | ``` 104 | 105 | [original document](http://redis.io/topics/internals-rediseventlib) 106 | -------------------------------------------------------------------------------- /example/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../src/ae.h" 8 | #include "../src/anet.h" 9 | 10 | void writeToClient(aeEventLoop *loop, int fd, void *clientdata, int mask) 11 | { 12 | char *buffer = clientdata; 13 | printf("recv client [%d] data: %s\n", fd, buffer); 14 | write(fd, buffer, strlen(buffer)); 15 | free(buffer); 16 | aeDeleteFileEvent(loop, fd, mask); 17 | } 18 | 19 | void readFromClient(aeEventLoop *loop, int fd, void *clientdata, int mask) 20 | { 21 | int buffer_size = 1024; 22 | char *buffer = calloc(buffer_size, sizeof(char)); 23 | int size; 24 | size = read(fd, buffer, buffer_size); 25 | if (size <= 0) 26 | { 27 | printf("Client disconnected\n"); 28 | free(buffer); 29 | aeDeleteFileEvent(loop, fd, AE_READABLE); 30 | return; 31 | } 32 | aeCreateFileEvent(loop, fd, AE_WRITABLE, writeToClient, buffer); 33 | } 34 | 35 | void acceptTcpHandler(aeEventLoop *loop, int fd, void *clientdata, int mask) 36 | { 37 | int client_port, client_fd; 38 | char client_ip[128]; 39 | // create client socket 40 | client_fd = anetTcpAccept(NULL, fd, client_ip, 128, &client_port); 41 | printf("Accepted %s:%d\n", client_ip, client_port); 42 | 43 | // set client socket non-block 44 | anetNonBlock(NULL, client_fd); 45 | 46 | // regist on message callback 47 | int ret; 48 | ret = aeCreateFileEvent(loop, client_fd, AE_READABLE, readFromClient, NULL); 49 | assert(ret != AE_ERR); 50 | } 51 | 52 | int main() 53 | { 54 | int ipfd; 55 | // create server socket 56 | ipfd = anetTcpServer(NULL, 8000, "0.0.0.0", 0); 57 | assert(ipfd != ANET_ERR); 58 | 59 | // create main event loop 60 | aeEventLoop *loop; 61 | loop = aeCreateEventLoop(1024); 62 | 63 | // regist socket connect callback 64 | int ret; 65 | ret = aeCreateFileEvent(loop, ipfd, AE_READABLE, acceptTcpHandler, NULL); 66 | assert(ret != AE_ERR); 67 | 68 | // start main loop 69 | aeMain(loop); 70 | 71 | // stop loop 72 | aeDeleteEventLoop(loop); 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /example/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../src/ae.h" 6 | 7 | #define BUFF_SIZE 512 8 | 9 | void freeClientData(struct aeEventLoop *eventLoop, void *clientData) 10 | { 11 | if(NULL != clientData) 12 | free(clientData); 13 | } 14 | 15 | int print(struct aeEventLoop *loop, long long id, void *clientData) 16 | { 17 | printf("event %lld - %s\n", id, (const char *)clientData); 18 | return -1; 19 | } 20 | 21 | int main(void) 22 | { 23 | aeEventLoop *loop = aeCreateEventLoop(10); 24 | int i; 25 | for (i = 1; i < 10; i ++) { 26 | char *eventData = calloc(BUFF_SIZE, sizeof(char)); 27 | if (NULL != eventData) 28 | { 29 | sprintf(eventData, "Hello World %d", i); 30 | aeCreateTimeEvent(loop, i*1000, print, eventData, freeClientData); 31 | } 32 | } 33 | aeMain(loop); 34 | aeDeleteEventLoop(loop); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libae", 3 | "version": "1.0.0", 4 | "repo": "aisk/libae", 5 | "keywords": [ 6 | "async", 7 | "eventloop", 8 | "redis", 9 | "net" 10 | ], 11 | "src": [ 12 | "src/anet.c", 13 | "src/anet.h", 14 | "src/ae.c", 15 | "src/ae.h", 16 | "src/ae_epoll.c", 17 | "src/ae_evport.c", 18 | "src/ae_kqueue.c", 19 | "src/ae_select.c", 20 | "src/config.h", 21 | "src/fmacros.h", 22 | "src/zmalloc.h" 23 | ], 24 | "makefile": "Makefile" 25 | } 26 | -------------------------------------------------------------------------------- /src/ae.c: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "ae.h" 44 | #include "zmalloc.h" 45 | #include "config.h" 46 | 47 | /* Include the best multiplexing layer supported by this system. 48 | * The following should be ordered by performances, descending. */ 49 | #ifdef HAVE_EVPORT 50 | #include "ae_evport.c" 51 | #else 52 | #ifdef HAVE_EPOLL 53 | #include "ae_epoll.c" 54 | #else 55 | #ifdef HAVE_KQUEUE 56 | #include "ae_kqueue.c" 57 | #else 58 | #include "ae_select.c" 59 | #endif 60 | #endif 61 | #endif 62 | 63 | aeEventLoop *aeCreateEventLoop(int setsize) { 64 | aeEventLoop *eventLoop; 65 | int i; 66 | 67 | if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err; 68 | eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize); 69 | eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize); 70 | if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err; 71 | eventLoop->setsize = setsize; 72 | eventLoop->lastTime = time(NULL); 73 | eventLoop->timeEventHead = NULL; 74 | eventLoop->timeEventNextId = 0; 75 | eventLoop->stop = 0; 76 | eventLoop->maxfd = -1; 77 | eventLoop->beforesleep = NULL; 78 | eventLoop->aftersleep = NULL; 79 | if (aeApiCreate(eventLoop) == -1) goto err; 80 | /* Events with mask == AE_NONE are not set. So let's initialize the 81 | * vector with it. */ 82 | for (i = 0; i < setsize; i++) 83 | eventLoop->events[i].mask = AE_NONE; 84 | return eventLoop; 85 | 86 | err: 87 | if (eventLoop) { 88 | zfree(eventLoop->events); 89 | zfree(eventLoop->fired); 90 | zfree(eventLoop); 91 | } 92 | return NULL; 93 | } 94 | 95 | /* Return the current set size. */ 96 | int aeGetSetSize(aeEventLoop *eventLoop) { 97 | return eventLoop->setsize; 98 | } 99 | 100 | /* Resize the maximum set size of the event loop. 101 | * If the requested set size is smaller than the current set size, but 102 | * there is already a file descriptor in use that is >= the requested 103 | * set size minus one, AE_ERR is returned and the operation is not 104 | * performed at all. 105 | * 106 | * Otherwise AE_OK is returned and the operation is successful. */ 107 | int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) { 108 | int i; 109 | 110 | if (setsize == eventLoop->setsize) return AE_OK; 111 | if (eventLoop->maxfd >= setsize) return AE_ERR; 112 | if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR; 113 | 114 | eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize); 115 | eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize); 116 | eventLoop->setsize = setsize; 117 | 118 | /* Make sure that if we created new slots, they are initialized with 119 | * an AE_NONE mask. */ 120 | for (i = eventLoop->maxfd+1; i < setsize; i++) 121 | eventLoop->events[i].mask = AE_NONE; 122 | return AE_OK; 123 | } 124 | 125 | void aeDeleteEventLoop(aeEventLoop *eventLoop) { 126 | aeApiFree(eventLoop); 127 | zfree(eventLoop->events); 128 | zfree(eventLoop->fired); 129 | zfree(eventLoop); 130 | } 131 | 132 | void aeStop(aeEventLoop *eventLoop) { 133 | eventLoop->stop = 1; 134 | } 135 | 136 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 137 | aeFileProc *proc, void *clientData) 138 | { 139 | if (fd >= eventLoop->setsize) { 140 | errno = ERANGE; 141 | return AE_ERR; 142 | } 143 | aeFileEvent *fe = &eventLoop->events[fd]; 144 | 145 | if (aeApiAddEvent(eventLoop, fd, mask) == -1) 146 | return AE_ERR; 147 | fe->mask |= mask; 148 | if (mask & AE_READABLE) fe->rfileProc = proc; 149 | if (mask & AE_WRITABLE) fe->wfileProc = proc; 150 | fe->clientData = clientData; 151 | if (fd > eventLoop->maxfd) 152 | eventLoop->maxfd = fd; 153 | return AE_OK; 154 | } 155 | 156 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) 157 | { 158 | if (fd >= eventLoop->setsize) return; 159 | aeFileEvent *fe = &eventLoop->events[fd]; 160 | if (fe->mask == AE_NONE) return; 161 | 162 | /* We want to always remove AE_BARRIER if set when AE_WRITABLE 163 | * is removed. */ 164 | if (mask & AE_WRITABLE) mask |= AE_BARRIER; 165 | 166 | aeApiDelEvent(eventLoop, fd, mask); 167 | fe->mask = fe->mask & (~mask); 168 | if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { 169 | /* Update the max fd */ 170 | int j; 171 | 172 | for (j = eventLoop->maxfd-1; j >= 0; j--) 173 | if (eventLoop->events[j].mask != AE_NONE) break; 174 | eventLoop->maxfd = j; 175 | } 176 | } 177 | 178 | int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { 179 | if (fd >= eventLoop->setsize) return 0; 180 | aeFileEvent *fe = &eventLoop->events[fd]; 181 | 182 | return fe->mask; 183 | } 184 | 185 | static void aeGetTime(long *seconds, long *milliseconds) 186 | { 187 | struct timeval tv; 188 | 189 | gettimeofday(&tv, NULL); 190 | *seconds = tv.tv_sec; 191 | *milliseconds = tv.tv_usec/1000; 192 | } 193 | 194 | static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) { 195 | long cur_sec, cur_ms, when_sec, when_ms; 196 | 197 | aeGetTime(&cur_sec, &cur_ms); 198 | when_sec = cur_sec + milliseconds/1000; 199 | when_ms = cur_ms + milliseconds%1000; 200 | if (when_ms >= 1000) { 201 | when_sec ++; 202 | when_ms -= 1000; 203 | } 204 | *sec = when_sec; 205 | *ms = when_ms; 206 | } 207 | 208 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 209 | aeTimeProc *proc, void *clientData, 210 | aeEventFinalizerProc *finalizerProc) 211 | { 212 | long long id = eventLoop->timeEventNextId++; 213 | aeTimeEvent *te; 214 | 215 | te = zmalloc(sizeof(*te)); 216 | if (te == NULL) return AE_ERR; 217 | te->id = id; 218 | aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 219 | te->timeProc = proc; 220 | te->finalizerProc = finalizerProc; 221 | te->clientData = clientData; 222 | te->prev = NULL; 223 | te->next = eventLoop->timeEventHead; 224 | if (te->next) 225 | te->next->prev = te; 226 | eventLoop->timeEventHead = te; 227 | return id; 228 | } 229 | 230 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) 231 | { 232 | aeTimeEvent *te = eventLoop->timeEventHead; 233 | while(te) { 234 | if (te->id == id) { 235 | te->id = AE_DELETED_EVENT_ID; 236 | return AE_OK; 237 | } 238 | te = te->next; 239 | } 240 | return AE_ERR; /* NO event with the specified ID found */ 241 | } 242 | 243 | /* Search the first timer to fire. 244 | * This operation is useful to know how many time the select can be 245 | * put in sleep without to delay any event. 246 | * If there are no timers NULL is returned. 247 | * 248 | * Note that's O(N) since time events are unsorted. 249 | * Possible optimizations (not needed by Redis so far, but...): 250 | * 1) Insert the event in order, so that the nearest is just the head. 251 | * Much better but still insertion or deletion of timers is O(N). 252 | * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). 253 | */ 254 | static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) 255 | { 256 | aeTimeEvent *te = eventLoop->timeEventHead; 257 | aeTimeEvent *nearest = NULL; 258 | 259 | while(te) { 260 | if (!nearest || te->when_sec < nearest->when_sec || 261 | (te->when_sec == nearest->when_sec && 262 | te->when_ms < nearest->when_ms)) 263 | nearest = te; 264 | te = te->next; 265 | } 266 | return nearest; 267 | } 268 | 269 | /* Process time events */ 270 | static int processTimeEvents(aeEventLoop *eventLoop) { 271 | int processed = 0; 272 | aeTimeEvent *te; 273 | long long maxId; 274 | time_t now = time(NULL); 275 | 276 | /* If the system clock is moved to the future, and then set back to the 277 | * right value, time events may be delayed in a random way. Often this 278 | * means that scheduled operations will not be performed soon enough. 279 | * 280 | * Here we try to detect system clock skews, and force all the time 281 | * events to be processed ASAP when this happens: the idea is that 282 | * processing events earlier is less dangerous than delaying them 283 | * indefinitely, and practice suggests it is. */ 284 | if (now < eventLoop->lastTime) { 285 | te = eventLoop->timeEventHead; 286 | while(te) { 287 | te->when_sec = 0; 288 | te = te->next; 289 | } 290 | } 291 | eventLoop->lastTime = now; 292 | 293 | te = eventLoop->timeEventHead; 294 | maxId = eventLoop->timeEventNextId-1; 295 | while(te) { 296 | long now_sec, now_ms; 297 | long long id; 298 | 299 | /* Remove events scheduled for deletion. */ 300 | if (te->id == AE_DELETED_EVENT_ID) { 301 | aeTimeEvent *next = te->next; 302 | if (te->prev) 303 | te->prev->next = te->next; 304 | else 305 | eventLoop->timeEventHead = te->next; 306 | if (te->next) 307 | te->next->prev = te->prev; 308 | if (te->finalizerProc) 309 | te->finalizerProc(eventLoop, te->clientData); 310 | zfree(te); 311 | te = next; 312 | continue; 313 | } 314 | 315 | /* Make sure we don't process time events created by time events in 316 | * this iteration. Note that this check is currently useless: we always 317 | * add new timers on the head, however if we change the implementation 318 | * detail, this check may be useful again: we keep it here for future 319 | * defense. */ 320 | if (te->id > maxId) { 321 | te = te->next; 322 | continue; 323 | } 324 | aeGetTime(&now_sec, &now_ms); 325 | if (now_sec > te->when_sec || 326 | (now_sec == te->when_sec && now_ms >= te->when_ms)) 327 | { 328 | int retval; 329 | 330 | id = te->id; 331 | retval = te->timeProc(eventLoop, id, te->clientData); 332 | processed++; 333 | if (retval != AE_NOMORE) { 334 | aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); 335 | } else { 336 | te->id = AE_DELETED_EVENT_ID; 337 | } 338 | } 339 | te = te->next; 340 | } 341 | return processed; 342 | } 343 | 344 | /* Process every pending time event, then every pending file event 345 | * (that may be registered by time event callbacks just processed). 346 | * Without special flags the function sleeps until some file event 347 | * fires, or when the next time event occurs (if any). 348 | * 349 | * If flags is 0, the function does nothing and returns. 350 | * if flags has AE_ALL_EVENTS set, all the kind of events are processed. 351 | * if flags has AE_FILE_EVENTS set, file events are processed. 352 | * if flags has AE_TIME_EVENTS set, time events are processed. 353 | * if flags has AE_DONT_WAIT set the function returns ASAP until all 354 | * if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called. 355 | * the events that's possible to process without to wait are processed. 356 | * 357 | * The function returns the number of events processed. */ 358 | int aeProcessEvents(aeEventLoop *eventLoop, int flags) 359 | { 360 | int processed = 0, numevents; 361 | 362 | /* Nothing to do? return ASAP */ 363 | if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; 364 | 365 | /* Note that we want call select() even if there are no 366 | * file events to process as long as we want to process time 367 | * events, in order to sleep until the next time event is ready 368 | * to fire. */ 369 | if (eventLoop->maxfd != -1 || 370 | ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { 371 | int j; 372 | aeTimeEvent *shortest = NULL; 373 | struct timeval tv, *tvp; 374 | 375 | if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) 376 | shortest = aeSearchNearestTimer(eventLoop); 377 | if (shortest) { 378 | long now_sec, now_ms; 379 | 380 | aeGetTime(&now_sec, &now_ms); 381 | tvp = &tv; 382 | 383 | /* How many milliseconds we need to wait for the next 384 | * time event to fire? */ 385 | long long ms = 386 | (shortest->when_sec - now_sec)*1000 + 387 | shortest->when_ms - now_ms; 388 | 389 | if (ms > 0) { 390 | tvp->tv_sec = ms/1000; 391 | tvp->tv_usec = (ms % 1000)*1000; 392 | } else { 393 | tvp->tv_sec = 0; 394 | tvp->tv_usec = 0; 395 | } 396 | } else { 397 | /* If we have to check for events but need to return 398 | * ASAP because of AE_DONT_WAIT we need to set the timeout 399 | * to zero */ 400 | if (flags & AE_DONT_WAIT) { 401 | tv.tv_sec = tv.tv_usec = 0; 402 | tvp = &tv; 403 | } else { 404 | /* Otherwise we can block */ 405 | tvp = NULL; /* wait forever */ 406 | } 407 | } 408 | 409 | /* Call the multiplexing API, will return only on timeout or when 410 | * some event fires. */ 411 | numevents = aeApiPoll(eventLoop, tvp); 412 | 413 | /* After sleep callback. */ 414 | if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP) 415 | eventLoop->aftersleep(eventLoop); 416 | 417 | for (j = 0; j < numevents; j++) { 418 | aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; 419 | int mask = eventLoop->fired[j].mask; 420 | int fd = eventLoop->fired[j].fd; 421 | int fired = 0; /* Number of events fired for current fd. */ 422 | 423 | /* Normally we execute the readable event first, and the writable 424 | * event laster. This is useful as sometimes we may be able 425 | * to serve the reply of a query immediately after processing the 426 | * query. 427 | * 428 | * However if AE_BARRIER is set in the mask, our application is 429 | * asking us to do the reverse: never fire the writable event 430 | * after the readable. In such a case, we invert the calls. 431 | * This is useful when, for instance, we want to do things 432 | * in the beforeSleep() hook, like fsynching a file to disk, 433 | * before replying to a client. */ 434 | int invert = fe->mask & AE_BARRIER; 435 | 436 | /* Note the "fe->mask & mask & ..." code: maybe an already 437 | * processed event removed an element that fired and we still 438 | * didn't processed, so we check if the event is still valid. 439 | * 440 | * Fire the readable event if the call sequence is not 441 | * inverted. */ 442 | if (!invert && fe->mask & mask & AE_READABLE) { 443 | fe->rfileProc(eventLoop,fd,fe->clientData,mask); 444 | fired++; 445 | } 446 | 447 | /* Fire the writable event. */ 448 | if (fe->mask & mask & AE_WRITABLE) { 449 | if (!fired || fe->wfileProc != fe->rfileProc) { 450 | fe->wfileProc(eventLoop,fd,fe->clientData,mask); 451 | fired++; 452 | } 453 | } 454 | 455 | /* If we have to invert the call, fire the readable event now 456 | * after the writable one. */ 457 | if (invert && fe->mask & mask & AE_READABLE) { 458 | if (!fired || fe->wfileProc != fe->rfileProc) { 459 | fe->rfileProc(eventLoop,fd,fe->clientData,mask); 460 | fired++; 461 | } 462 | } 463 | 464 | processed++; 465 | } 466 | } 467 | /* Check time events */ 468 | if (flags & AE_TIME_EVENTS) 469 | processed += processTimeEvents(eventLoop); 470 | 471 | return processed; /* return the number of processed file/time events */ 472 | } 473 | 474 | /* Wait for milliseconds until the given file descriptor becomes 475 | * writable/readable/exception */ 476 | int aeWait(int fd, int mask, long long milliseconds) { 477 | struct pollfd pfd; 478 | int retmask = 0, retval; 479 | 480 | memset(&pfd, 0, sizeof(pfd)); 481 | pfd.fd = fd; 482 | if (mask & AE_READABLE) pfd.events |= POLLIN; 483 | if (mask & AE_WRITABLE) pfd.events |= POLLOUT; 484 | 485 | if ((retval = poll(&pfd, 1, milliseconds))== 1) { 486 | if (pfd.revents & POLLIN) retmask |= AE_READABLE; 487 | if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE; 488 | if (pfd.revents & POLLERR) retmask |= AE_WRITABLE; 489 | if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE; 490 | return retmask; 491 | } else { 492 | return retval; 493 | } 494 | } 495 | 496 | void aeMain(aeEventLoop *eventLoop) { 497 | eventLoop->stop = 0; 498 | while (!eventLoop->stop) { 499 | if (eventLoop->beforesleep != NULL) 500 | eventLoop->beforesleep(eventLoop); 501 | aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP); 502 | } 503 | } 504 | 505 | char *aeGetApiName(void) { 506 | return aeApiName(); 507 | } 508 | 509 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) { 510 | eventLoop->beforesleep = beforesleep; 511 | } 512 | 513 | void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) { 514 | eventLoop->aftersleep = aftersleep; 515 | } 516 | -------------------------------------------------------------------------------- /src/ae.h: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __AE_H__ 34 | #define __AE_H__ 35 | 36 | #include 37 | 38 | #define AE_OK 0 39 | #define AE_ERR -1 40 | 41 | #define AE_NONE 0 /* No events registered. */ 42 | #define AE_READABLE 1 /* Fire when descriptor is readable. */ 43 | #define AE_WRITABLE 2 /* Fire when descriptor is writable. */ 44 | #define AE_BARRIER 4 /* With WRITABLE, never fire the event if the 45 | READABLE event already fired in the same event 46 | loop iteration. Useful when you want to persist 47 | things to disk before sending replies, and want 48 | to do that in a group fashion. */ 49 | 50 | #define AE_FILE_EVENTS 1 51 | #define AE_TIME_EVENTS 2 52 | #define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS) 53 | #define AE_DONT_WAIT 4 54 | #define AE_CALL_AFTER_SLEEP 8 55 | 56 | #define AE_NOMORE -1 57 | #define AE_DELETED_EVENT_ID -1 58 | 59 | /* Macros */ 60 | #define AE_NOTUSED(V) ((void) V) 61 | 62 | struct aeEventLoop; 63 | 64 | /* Types and data structures */ 65 | typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); 66 | typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData); 67 | typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData); 68 | typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop); 69 | 70 | /* File event structure */ 71 | typedef struct aeFileEvent { 72 | int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */ 73 | aeFileProc *rfileProc; 74 | aeFileProc *wfileProc; 75 | void *clientData; 76 | } aeFileEvent; 77 | 78 | /* Time event structure */ 79 | typedef struct aeTimeEvent { 80 | long long id; /* time event identifier. */ 81 | long when_sec; /* seconds */ 82 | long when_ms; /* milliseconds */ 83 | aeTimeProc *timeProc; 84 | aeEventFinalizerProc *finalizerProc; 85 | void *clientData; 86 | struct aeTimeEvent *prev; 87 | struct aeTimeEvent *next; 88 | } aeTimeEvent; 89 | 90 | /* A fired event */ 91 | typedef struct aeFiredEvent { 92 | int fd; 93 | int mask; 94 | } aeFiredEvent; 95 | 96 | /* State of an event based program */ 97 | typedef struct aeEventLoop { 98 | int maxfd; /* highest file descriptor currently registered */ 99 | int setsize; /* max number of file descriptors tracked */ 100 | long long timeEventNextId; 101 | time_t lastTime; /* Used to detect system clock skew */ 102 | aeFileEvent *events; /* Registered events */ 103 | aeFiredEvent *fired; /* Fired events */ 104 | aeTimeEvent *timeEventHead; 105 | int stop; 106 | void *apidata; /* This is used for polling API specific data */ 107 | aeBeforeSleepProc *beforesleep; 108 | aeBeforeSleepProc *aftersleep; 109 | } aeEventLoop; 110 | 111 | /* Prototypes */ 112 | aeEventLoop *aeCreateEventLoop(int setsize); 113 | void aeDeleteEventLoop(aeEventLoop *eventLoop); 114 | void aeStop(aeEventLoop *eventLoop); 115 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 116 | aeFileProc *proc, void *clientData); 117 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask); 118 | int aeGetFileEvents(aeEventLoop *eventLoop, int fd); 119 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 120 | aeTimeProc *proc, void *clientData, 121 | aeEventFinalizerProc *finalizerProc); 122 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id); 123 | int aeProcessEvents(aeEventLoop *eventLoop, int flags); 124 | int aeWait(int fd, int mask, long long milliseconds); 125 | void aeMain(aeEventLoop *eventLoop); 126 | char *aeGetApiName(void); 127 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep); 128 | void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep); 129 | int aeGetSetSize(aeEventLoop *eventLoop); 130 | int aeResizeSetSize(aeEventLoop *eventLoop, int setsize); 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /src/ae_epoll.c: -------------------------------------------------------------------------------- 1 | /* Linux epoll(2) based ae.c module 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | 34 | typedef struct aeApiState { 35 | int epfd; 36 | struct epoll_event *events; 37 | } aeApiState; 38 | 39 | static int aeApiCreate(aeEventLoop *eventLoop) { 40 | aeApiState *state = zmalloc(sizeof(aeApiState)); 41 | 42 | if (!state) return -1; 43 | state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize); 44 | if (!state->events) { 45 | zfree(state); 46 | return -1; 47 | } 48 | state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */ 49 | if (state->epfd == -1) { 50 | zfree(state->events); 51 | zfree(state); 52 | return -1; 53 | } 54 | eventLoop->apidata = state; 55 | return 0; 56 | } 57 | 58 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 59 | aeApiState *state = eventLoop->apidata; 60 | 61 | state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize); 62 | return 0; 63 | } 64 | 65 | static void aeApiFree(aeEventLoop *eventLoop) { 66 | aeApiState *state = eventLoop->apidata; 67 | 68 | close(state->epfd); 69 | zfree(state->events); 70 | zfree(state); 71 | } 72 | 73 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 74 | aeApiState *state = eventLoop->apidata; 75 | struct epoll_event ee = {0}; /* avoid valgrind warning */ 76 | /* If the fd was already monitored for some event, we need a MOD 77 | * operation. Otherwise we need an ADD operation. */ 78 | int op = eventLoop->events[fd].mask == AE_NONE ? 79 | EPOLL_CTL_ADD : EPOLL_CTL_MOD; 80 | 81 | ee.events = 0; 82 | mask |= eventLoop->events[fd].mask; /* Merge old events */ 83 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 84 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 85 | ee.data.fd = fd; 86 | if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; 87 | return 0; 88 | } 89 | 90 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { 91 | aeApiState *state = eventLoop->apidata; 92 | struct epoll_event ee = {0}; /* avoid valgrind warning */ 93 | int mask = eventLoop->events[fd].mask & (~delmask); 94 | 95 | ee.events = 0; 96 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 97 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 98 | ee.data.fd = fd; 99 | if (mask != AE_NONE) { 100 | epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); 101 | } else { 102 | /* Note, Kernel < 2.6.9 requires a non null event pointer even for 103 | * EPOLL_CTL_DEL. */ 104 | epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee); 105 | } 106 | } 107 | 108 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 109 | aeApiState *state = eventLoop->apidata; 110 | int retval, numevents = 0; 111 | 112 | retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, 113 | tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); 114 | if (retval > 0) { 115 | int j; 116 | 117 | numevents = retval; 118 | for (j = 0; j < numevents; j++) { 119 | int mask = 0; 120 | struct epoll_event *e = state->events+j; 121 | 122 | if (e->events & EPOLLIN) mask |= AE_READABLE; 123 | if (e->events & EPOLLOUT) mask |= AE_WRITABLE; 124 | if (e->events & EPOLLERR) mask |= AE_WRITABLE; 125 | if (e->events & EPOLLHUP) mask |= AE_WRITABLE; 126 | eventLoop->fired[j].fd = e->data.fd; 127 | eventLoop->fired[j].mask = mask; 128 | } 129 | } 130 | return numevents; 131 | } 132 | 133 | static char *aeApiName(void) { 134 | return "epoll"; 135 | } 136 | -------------------------------------------------------------------------------- /src/ae_evport.c: -------------------------------------------------------------------------------- 1 | /* ae.c module for illumos event ports. 2 | * 3 | * Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | static int evport_debug = 0; 42 | 43 | /* 44 | * This file implements the ae API using event ports, present on Solaris-based 45 | * systems since Solaris 10. Using the event port interface, we associate file 46 | * descriptors with the port. Each association also includes the set of poll(2) 47 | * events that the consumer is interested in (e.g., POLLIN and POLLOUT). 48 | * 49 | * There's one tricky piece to this implementation: when we return events via 50 | * aeApiPoll, the corresponding file descriptors become dissociated from the 51 | * port. This is necessary because poll events are level-triggered, so if the 52 | * fd didn't become dissociated, it would immediately fire another event since 53 | * the underlying state hasn't changed yet. We must re-associate the file 54 | * descriptor, but only after we know that our caller has actually read from it. 55 | * The ae API does not tell us exactly when that happens, but we do know that 56 | * it must happen by the time aeApiPoll is called again. Our solution is to 57 | * keep track of the last fds returned by aeApiPoll and re-associate them next 58 | * time aeApiPoll is invoked. 59 | * 60 | * To summarize, in this module, each fd association is EITHER (a) represented 61 | * only via the in-kernel association OR (b) represented by pending_fds and 62 | * pending_masks. (b) is only true for the last fds we returned from aeApiPoll, 63 | * and only until we enter aeApiPoll again (at which point we restore the 64 | * in-kernel association). 65 | */ 66 | #define MAX_EVENT_BATCHSZ 512 67 | 68 | typedef struct aeApiState { 69 | int portfd; /* event port */ 70 | int npending; /* # of pending fds */ 71 | int pending_fds[MAX_EVENT_BATCHSZ]; /* pending fds */ 72 | int pending_masks[MAX_EVENT_BATCHSZ]; /* pending fds' masks */ 73 | } aeApiState; 74 | 75 | static int aeApiCreate(aeEventLoop *eventLoop) { 76 | int i; 77 | aeApiState *state = zmalloc(sizeof(aeApiState)); 78 | if (!state) return -1; 79 | 80 | state->portfd = port_create(); 81 | if (state->portfd == -1) { 82 | zfree(state); 83 | return -1; 84 | } 85 | 86 | state->npending = 0; 87 | 88 | for (i = 0; i < MAX_EVENT_BATCHSZ; i++) { 89 | state->pending_fds[i] = -1; 90 | state->pending_masks[i] = AE_NONE; 91 | } 92 | 93 | eventLoop->apidata = state; 94 | return 0; 95 | } 96 | 97 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 98 | /* Nothing to resize here. */ 99 | return 0; 100 | } 101 | 102 | static void aeApiFree(aeEventLoop *eventLoop) { 103 | aeApiState *state = eventLoop->apidata; 104 | 105 | close(state->portfd); 106 | zfree(state); 107 | } 108 | 109 | static int aeApiLookupPending(aeApiState *state, int fd) { 110 | int i; 111 | 112 | for (i = 0; i < state->npending; i++) { 113 | if (state->pending_fds[i] == fd) 114 | return (i); 115 | } 116 | 117 | return (-1); 118 | } 119 | 120 | /* 121 | * Helper function to invoke port_associate for the given fd and mask. 122 | */ 123 | static int aeApiAssociate(const char *where, int portfd, int fd, int mask) { 124 | int events = 0; 125 | int rv, err; 126 | 127 | if (mask & AE_READABLE) 128 | events |= POLLIN; 129 | if (mask & AE_WRITABLE) 130 | events |= POLLOUT; 131 | 132 | if (evport_debug) 133 | fprintf(stderr, "%s: port_associate(%d, 0x%x) = ", where, fd, events); 134 | 135 | rv = port_associate(portfd, PORT_SOURCE_FD, fd, events, 136 | (void *)(uintptr_t)mask); 137 | err = errno; 138 | 139 | if (evport_debug) 140 | fprintf(stderr, "%d (%s)\n", rv, rv == 0 ? "no error" : strerror(err)); 141 | 142 | if (rv == -1) { 143 | fprintf(stderr, "%s: port_associate: %s\n", where, strerror(err)); 144 | 145 | if (err == EAGAIN) 146 | fprintf(stderr, "aeApiAssociate: event port limit exceeded."); 147 | } 148 | 149 | return rv; 150 | } 151 | 152 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 153 | aeApiState *state = eventLoop->apidata; 154 | int fullmask, pfd; 155 | 156 | if (evport_debug) 157 | fprintf(stderr, "aeApiAddEvent: fd %d mask 0x%x\n", fd, mask); 158 | 159 | /* 160 | * Since port_associate's "events" argument replaces any existing events, we 161 | * must be sure to include whatever events are already associated when 162 | * we call port_associate() again. 163 | */ 164 | fullmask = mask | eventLoop->events[fd].mask; 165 | pfd = aeApiLookupPending(state, fd); 166 | 167 | if (pfd != -1) { 168 | /* 169 | * This fd was recently returned from aeApiPoll. It should be safe to 170 | * assume that the consumer has processed that poll event, but we play 171 | * it safer by simply updating pending_mask. The fd will be 172 | * re-associated as usual when aeApiPoll is called again. 173 | */ 174 | if (evport_debug) 175 | fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd); 176 | state->pending_masks[pfd] |= fullmask; 177 | return 0; 178 | } 179 | 180 | return (aeApiAssociate("aeApiAddEvent", state->portfd, fd, fullmask)); 181 | } 182 | 183 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 184 | aeApiState *state = eventLoop->apidata; 185 | int fullmask, pfd; 186 | 187 | if (evport_debug) 188 | fprintf(stderr, "del fd %d mask 0x%x\n", fd, mask); 189 | 190 | pfd = aeApiLookupPending(state, fd); 191 | 192 | if (pfd != -1) { 193 | if (evport_debug) 194 | fprintf(stderr, "deleting event from pending fd %d\n", fd); 195 | 196 | /* 197 | * This fd was just returned from aeApiPoll, so it's not currently 198 | * associated with the port. All we need to do is update 199 | * pending_mask appropriately. 200 | */ 201 | state->pending_masks[pfd] &= ~mask; 202 | 203 | if (state->pending_masks[pfd] == AE_NONE) 204 | state->pending_fds[pfd] = -1; 205 | 206 | return; 207 | } 208 | 209 | /* 210 | * The fd is currently associated with the port. Like with the add case 211 | * above, we must look at the full mask for the file descriptor before 212 | * updating that association. We don't have a good way of knowing what the 213 | * events are without looking into the eventLoop state directly. We rely on 214 | * the fact that our caller has already updated the mask in the eventLoop. 215 | */ 216 | 217 | fullmask = eventLoop->events[fd].mask; 218 | if (fullmask == AE_NONE) { 219 | /* 220 | * We're removing *all* events, so use port_dissociate to remove the 221 | * association completely. Failure here indicates a bug. 222 | */ 223 | if (evport_debug) 224 | fprintf(stderr, "aeApiDelEvent: port_dissociate(%d)\n", fd); 225 | 226 | if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) { 227 | perror("aeApiDelEvent: port_dissociate"); 228 | abort(); /* will not return */ 229 | } 230 | } else if (aeApiAssociate("aeApiDelEvent", state->portfd, fd, 231 | fullmask) != 0) { 232 | /* 233 | * ENOMEM is a potentially transient condition, but the kernel won't 234 | * generally return it unless things are really bad. EAGAIN indicates 235 | * we've reached an resource limit, for which it doesn't make sense to 236 | * retry (counter-intuitively). All other errors indicate a bug. In any 237 | * of these cases, the best we can do is to abort. 238 | */ 239 | abort(); /* will not return */ 240 | } 241 | } 242 | 243 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 244 | aeApiState *state = eventLoop->apidata; 245 | struct timespec timeout, *tsp; 246 | int mask, i; 247 | uint_t nevents; 248 | port_event_t event[MAX_EVENT_BATCHSZ]; 249 | 250 | /* 251 | * If we've returned fd events before, we must re-associate them with the 252 | * port now, before calling port_get(). See the block comment at the top of 253 | * this file for an explanation of why. 254 | */ 255 | for (i = 0; i < state->npending; i++) { 256 | if (state->pending_fds[i] == -1) 257 | /* This fd has since been deleted. */ 258 | continue; 259 | 260 | if (aeApiAssociate("aeApiPoll", state->portfd, 261 | state->pending_fds[i], state->pending_masks[i]) != 0) { 262 | /* See aeApiDelEvent for why this case is fatal. */ 263 | abort(); 264 | } 265 | 266 | state->pending_masks[i] = AE_NONE; 267 | state->pending_fds[i] = -1; 268 | } 269 | 270 | state->npending = 0; 271 | 272 | if (tvp != NULL) { 273 | timeout.tv_sec = tvp->tv_sec; 274 | timeout.tv_nsec = tvp->tv_usec * 1000; 275 | tsp = &timeout; 276 | } else { 277 | tsp = NULL; 278 | } 279 | 280 | /* 281 | * port_getn can return with errno == ETIME having returned some events (!). 282 | * So if we get ETIME, we check nevents, too. 283 | */ 284 | nevents = 1; 285 | if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents, 286 | tsp) == -1 && (errno != ETIME || nevents == 0)) { 287 | if (errno == ETIME || errno == EINTR) 288 | return 0; 289 | 290 | /* Any other error indicates a bug. */ 291 | perror("aeApiPoll: port_get"); 292 | abort(); 293 | } 294 | 295 | state->npending = nevents; 296 | 297 | for (i = 0; i < nevents; i++) { 298 | mask = 0; 299 | if (event[i].portev_events & POLLIN) 300 | mask |= AE_READABLE; 301 | if (event[i].portev_events & POLLOUT) 302 | mask |= AE_WRITABLE; 303 | 304 | eventLoop->fired[i].fd = event[i].portev_object; 305 | eventLoop->fired[i].mask = mask; 306 | 307 | if (evport_debug) 308 | fprintf(stderr, "aeApiPoll: fd %d mask 0x%x\n", 309 | (int)event[i].portev_object, mask); 310 | 311 | state->pending_fds[i] = event[i].portev_object; 312 | state->pending_masks[i] = (uintptr_t)event[i].portev_user; 313 | } 314 | 315 | return nevents; 316 | } 317 | 318 | static char *aeApiName(void) { 319 | return "evport"; 320 | } 321 | -------------------------------------------------------------------------------- /src/ae_kqueue.c: -------------------------------------------------------------------------------- 1 | /* Kqueue(2)-based ae.c module 2 | * 3 | * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | typedef struct aeApiState { 37 | int kqfd; 38 | struct kevent *events; 39 | } aeApiState; 40 | 41 | static int aeApiCreate(aeEventLoop *eventLoop) { 42 | aeApiState *state = zmalloc(sizeof(aeApiState)); 43 | 44 | if (!state) return -1; 45 | state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize); 46 | if (!state->events) { 47 | zfree(state); 48 | return -1; 49 | } 50 | state->kqfd = kqueue(); 51 | if (state->kqfd == -1) { 52 | zfree(state->events); 53 | zfree(state); 54 | return -1; 55 | } 56 | eventLoop->apidata = state; 57 | return 0; 58 | } 59 | 60 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 61 | aeApiState *state = eventLoop->apidata; 62 | 63 | state->events = zrealloc(state->events, sizeof(struct kevent)*setsize); 64 | return 0; 65 | } 66 | 67 | static void aeApiFree(aeEventLoop *eventLoop) { 68 | aeApiState *state = eventLoop->apidata; 69 | 70 | close(state->kqfd); 71 | zfree(state->events); 72 | zfree(state); 73 | } 74 | 75 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 76 | aeApiState *state = eventLoop->apidata; 77 | struct kevent ke; 78 | 79 | if (mask & AE_READABLE) { 80 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 81 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 82 | } 83 | if (mask & AE_WRITABLE) { 84 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 85 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 91 | aeApiState *state = eventLoop->apidata; 92 | struct kevent ke; 93 | 94 | if (mask & AE_READABLE) { 95 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 96 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 97 | } 98 | if (mask & AE_WRITABLE) { 99 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 100 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 101 | } 102 | } 103 | 104 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 105 | aeApiState *state = eventLoop->apidata; 106 | int retval, numevents = 0; 107 | 108 | if (tvp != NULL) { 109 | struct timespec timeout; 110 | timeout.tv_sec = tvp->tv_sec; 111 | timeout.tv_nsec = tvp->tv_usec * 1000; 112 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 113 | &timeout); 114 | } else { 115 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 116 | NULL); 117 | } 118 | 119 | if (retval > 0) { 120 | int j; 121 | 122 | numevents = retval; 123 | for(j = 0; j < numevents; j++) { 124 | int mask = 0; 125 | struct kevent *e = state->events+j; 126 | 127 | if (e->filter == EVFILT_READ) mask |= AE_READABLE; 128 | if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE; 129 | eventLoop->fired[j].fd = e->ident; 130 | eventLoop->fired[j].mask = mask; 131 | } 132 | } 133 | return numevents; 134 | } 135 | 136 | static char *aeApiName(void) { 137 | return "kqueue"; 138 | } 139 | -------------------------------------------------------------------------------- /src/ae_select.c: -------------------------------------------------------------------------------- 1 | /* Select()-based ae.c module. 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | #include 34 | 35 | typedef struct aeApiState { 36 | fd_set rfds, wfds; 37 | /* We need to have a copy of the fd sets as it's not safe to reuse 38 | * FD sets after select(). */ 39 | fd_set _rfds, _wfds; 40 | } aeApiState; 41 | 42 | static int aeApiCreate(aeEventLoop *eventLoop) { 43 | aeApiState *state = zmalloc(sizeof(aeApiState)); 44 | 45 | if (!state) return -1; 46 | FD_ZERO(&state->rfds); 47 | FD_ZERO(&state->wfds); 48 | eventLoop->apidata = state; 49 | return 0; 50 | } 51 | 52 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 53 | /* Just ensure we have enough room in the fd_set type. */ 54 | if (setsize >= FD_SETSIZE) return -1; 55 | return 0; 56 | } 57 | 58 | static void aeApiFree(aeEventLoop *eventLoop) { 59 | zfree(eventLoop->apidata); 60 | } 61 | 62 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 63 | aeApiState *state = eventLoop->apidata; 64 | 65 | if (mask & AE_READABLE) FD_SET(fd,&state->rfds); 66 | if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds); 67 | return 0; 68 | } 69 | 70 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 71 | aeApiState *state = eventLoop->apidata; 72 | 73 | if (mask & AE_READABLE) FD_CLR(fd,&state->rfds); 74 | if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds); 75 | } 76 | 77 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 78 | aeApiState *state = eventLoop->apidata; 79 | int retval, j, numevents = 0; 80 | 81 | memcpy(&state->_rfds,&state->rfds,sizeof(fd_set)); 82 | memcpy(&state->_wfds,&state->wfds,sizeof(fd_set)); 83 | 84 | retval = select(eventLoop->maxfd+1, 85 | &state->_rfds,&state->_wfds,NULL,tvp); 86 | if (retval > 0) { 87 | for (j = 0; j <= eventLoop->maxfd; j++) { 88 | int mask = 0; 89 | aeFileEvent *fe = &eventLoop->events[j]; 90 | 91 | if (fe->mask == AE_NONE) continue; 92 | if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds)) 93 | mask |= AE_READABLE; 94 | if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds)) 95 | mask |= AE_WRITABLE; 96 | eventLoop->fired[numevents].fd = j; 97 | eventLoop->fired[numevents].mask = mask; 98 | numevents++; 99 | } 100 | } 101 | return numevents; 102 | } 103 | 104 | static char *aeApiName(void) { 105 | return "select"; 106 | } 107 | -------------------------------------------------------------------------------- /src/anet.c: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "fmacros.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "anet.h" 50 | 51 | static void anetSetError(char *err, const char *fmt, ...) 52 | { 53 | va_list ap; 54 | 55 | if (!err) return; 56 | va_start(ap, fmt); 57 | vsnprintf(err, ANET_ERR_LEN, fmt, ap); 58 | va_end(ap); 59 | } 60 | 61 | int anetSetBlock(char *err, int fd, int non_block) { 62 | int flags; 63 | 64 | /* Set the socket blocking (if non_block is zero) or non-blocking. 65 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be 66 | * interrupted by a signal. */ 67 | if ((flags = fcntl(fd, F_GETFL)) == -1) { 68 | anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 69 | return ANET_ERR; 70 | } 71 | 72 | if (non_block) 73 | flags |= O_NONBLOCK; 74 | else 75 | flags &= ~O_NONBLOCK; 76 | 77 | if (fcntl(fd, F_SETFL, flags) == -1) { 78 | anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); 79 | return ANET_ERR; 80 | } 81 | return ANET_OK; 82 | } 83 | 84 | int anetNonBlock(char *err, int fd) { 85 | return anetSetBlock(err,fd,1); 86 | } 87 | 88 | int anetBlock(char *err, int fd) { 89 | return anetSetBlock(err,fd,0); 90 | } 91 | 92 | /* Set TCP keep alive option to detect dead peers. The interval option 93 | * is only used for Linux as we are using Linux-specific APIs to set 94 | * the probe send time, interval, and count. */ 95 | int anetKeepAlive(char *err, int fd, int interval) 96 | { 97 | int val = 1; 98 | 99 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) 100 | { 101 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 102 | return ANET_ERR; 103 | } 104 | 105 | #ifdef __linux__ 106 | /* Default settings are more or less garbage, with the keepalive time 107 | * set to 7200 by default on Linux. Modify settings to make the feature 108 | * actually useful. */ 109 | 110 | /* Send first probe after interval. */ 111 | val = interval; 112 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { 113 | anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno)); 114 | return ANET_ERR; 115 | } 116 | 117 | /* Send next probes after the specified interval. Note that we set the 118 | * delay as interval / 3, as we send three probes before detecting 119 | * an error (see the next setsockopt call). */ 120 | val = interval/3; 121 | if (val == 0) val = 1; 122 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { 123 | anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); 124 | return ANET_ERR; 125 | } 126 | 127 | /* Consider the socket in error state after three we send three ACK 128 | * probes without getting a reply. */ 129 | val = 3; 130 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { 131 | anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); 132 | return ANET_ERR; 133 | } 134 | #else 135 | ((void) interval); /* Avoid unused var warning for non Linux systems. */ 136 | #endif 137 | 138 | return ANET_OK; 139 | } 140 | 141 | static int anetSetTcpNoDelay(char *err, int fd, int val) 142 | { 143 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1) 144 | { 145 | anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); 146 | return ANET_ERR; 147 | } 148 | return ANET_OK; 149 | } 150 | 151 | int anetEnableTcpNoDelay(char *err, int fd) 152 | { 153 | return anetSetTcpNoDelay(err, fd, 1); 154 | } 155 | 156 | int anetDisableTcpNoDelay(char *err, int fd) 157 | { 158 | return anetSetTcpNoDelay(err, fd, 0); 159 | } 160 | 161 | 162 | int anetSetSendBuffer(char *err, int fd, int buffsize) 163 | { 164 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) 165 | { 166 | anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); 167 | return ANET_ERR; 168 | } 169 | return ANET_OK; 170 | } 171 | 172 | int anetTcpKeepAlive(char *err, int fd) 173 | { 174 | int yes = 1; 175 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { 176 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 177 | return ANET_ERR; 178 | } 179 | return ANET_OK; 180 | } 181 | 182 | /* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified 183 | * number of milliseconds, or disable it if the 'ms' argument is zero. */ 184 | int anetSendTimeout(char *err, int fd, long long ms) { 185 | struct timeval tv; 186 | 187 | tv.tv_sec = ms/1000; 188 | tv.tv_usec = (ms%1000)*1000; 189 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) { 190 | anetSetError(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno)); 191 | return ANET_ERR; 192 | } 193 | return ANET_OK; 194 | } 195 | 196 | /* anetGenericResolve() is called by anetResolve() and anetResolveIP() to 197 | * do the actual work. It resolves the hostname "host" and set the string 198 | * representation of the IP address into the buffer pointed by "ipbuf". 199 | * 200 | * If flags is set to ANET_IP_ONLY the function only resolves hostnames 201 | * that are actually already IPv4 or IPv6 addresses. This turns the function 202 | * into a validating / normalizing function. */ 203 | int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, 204 | int flags) 205 | { 206 | struct addrinfo hints, *info; 207 | int rv; 208 | 209 | memset(&hints,0,sizeof(hints)); 210 | if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST; 211 | hints.ai_family = AF_UNSPEC; 212 | hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */ 213 | 214 | if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) { 215 | anetSetError(err, "%s", gai_strerror(rv)); 216 | return ANET_ERR; 217 | } 218 | if (info->ai_family == AF_INET) { 219 | struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr; 220 | inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len); 221 | } else { 222 | struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr; 223 | inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len); 224 | } 225 | 226 | freeaddrinfo(info); 227 | return ANET_OK; 228 | } 229 | 230 | int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len) { 231 | return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_NONE); 232 | } 233 | 234 | int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len) { 235 | return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_IP_ONLY); 236 | } 237 | 238 | static int anetSetReuseAddr(char *err, int fd) { 239 | int yes = 1; 240 | /* Make sure connection-intensive things like the redis benchmark 241 | * will be able to close/open sockets a zillion of times */ 242 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { 243 | anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); 244 | return ANET_ERR; 245 | } 246 | return ANET_OK; 247 | } 248 | 249 | static int anetCreateSocket(char *err, int domain) { 250 | int s; 251 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { 252 | anetSetError(err, "creating socket: %s", strerror(errno)); 253 | return ANET_ERR; 254 | } 255 | 256 | /* Make sure connection-intensive things like the redis benchmark 257 | * will be able to close/open sockets a zillion of times */ 258 | if (anetSetReuseAddr(err,s) == ANET_ERR) { 259 | close(s); 260 | return ANET_ERR; 261 | } 262 | return s; 263 | } 264 | 265 | #define ANET_CONNECT_NONE 0 266 | #define ANET_CONNECT_NONBLOCK 1 267 | #define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */ 268 | static int anetTcpGenericConnect(char *err, char *addr, int port, 269 | char *source_addr, int flags) 270 | { 271 | int s = ANET_ERR, rv; 272 | char portstr[6]; /* strlen("65535") + 1; */ 273 | struct addrinfo hints, *servinfo, *bservinfo, *p, *b; 274 | 275 | snprintf(portstr,sizeof(portstr),"%d",port); 276 | memset(&hints,0,sizeof(hints)); 277 | hints.ai_family = AF_UNSPEC; 278 | hints.ai_socktype = SOCK_STREAM; 279 | 280 | if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) { 281 | anetSetError(err, "%s", gai_strerror(rv)); 282 | return ANET_ERR; 283 | } 284 | for (p = servinfo; p != NULL; p = p->ai_next) { 285 | /* Try to create the socket and to connect it. 286 | * If we fail in the socket() call, or on connect(), we retry with 287 | * the next entry in servinfo. */ 288 | if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) 289 | continue; 290 | if (anetSetReuseAddr(err,s) == ANET_ERR) goto error; 291 | if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK) 292 | goto error; 293 | if (source_addr) { 294 | int bound = 0; 295 | /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ 296 | if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) 297 | { 298 | anetSetError(err, "%s", gai_strerror(rv)); 299 | goto error; 300 | } 301 | for (b = bservinfo; b != NULL; b = b->ai_next) { 302 | if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { 303 | bound = 1; 304 | break; 305 | } 306 | } 307 | freeaddrinfo(bservinfo); 308 | if (!bound) { 309 | anetSetError(err, "bind: %s", strerror(errno)); 310 | goto error; 311 | } 312 | } 313 | if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { 314 | /* If the socket is non-blocking, it is ok for connect() to 315 | * return an EINPROGRESS error here. */ 316 | if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) 317 | goto end; 318 | close(s); 319 | s = ANET_ERR; 320 | continue; 321 | } 322 | 323 | /* If we ended an iteration of the for loop without errors, we 324 | * have a connected socket. Let's return to the caller. */ 325 | goto end; 326 | } 327 | if (p == NULL) 328 | anetSetError(err, "creating socket: %s", strerror(errno)); 329 | 330 | error: 331 | if (s != ANET_ERR) { 332 | close(s); 333 | s = ANET_ERR; 334 | } 335 | 336 | end: 337 | freeaddrinfo(servinfo); 338 | 339 | /* Handle best effort binding: if a binding address was used, but it is 340 | * not possible to create a socket, try again without a binding address. */ 341 | if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) { 342 | return anetTcpGenericConnect(err,addr,port,NULL,flags); 343 | } else { 344 | return s; 345 | } 346 | } 347 | 348 | int anetTcpConnect(char *err, char *addr, int port) 349 | { 350 | return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE); 351 | } 352 | 353 | int anetTcpNonBlockConnect(char *err, char *addr, int port) 354 | { 355 | return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK); 356 | } 357 | 358 | int anetTcpNonBlockBindConnect(char *err, char *addr, int port, 359 | char *source_addr) 360 | { 361 | return anetTcpGenericConnect(err,addr,port,source_addr, 362 | ANET_CONNECT_NONBLOCK); 363 | } 364 | 365 | int anetTcpNonBlockBestEffortBindConnect(char *err, char *addr, int port, 366 | char *source_addr) 367 | { 368 | return anetTcpGenericConnect(err,addr,port,source_addr, 369 | ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING); 370 | } 371 | 372 | int anetUnixGenericConnect(char *err, char *path, int flags) 373 | { 374 | int s; 375 | struct sockaddr_un sa; 376 | 377 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 378 | return ANET_ERR; 379 | 380 | sa.sun_family = AF_LOCAL; 381 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 382 | if (flags & ANET_CONNECT_NONBLOCK) { 383 | if (anetNonBlock(err,s) != ANET_OK) { 384 | close(s); 385 | return ANET_ERR; 386 | } 387 | } 388 | if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { 389 | if (errno == EINPROGRESS && 390 | flags & ANET_CONNECT_NONBLOCK) 391 | return s; 392 | 393 | anetSetError(err, "connect: %s", strerror(errno)); 394 | close(s); 395 | return ANET_ERR; 396 | } 397 | return s; 398 | } 399 | 400 | int anetUnixConnect(char *err, char *path) 401 | { 402 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE); 403 | } 404 | 405 | int anetUnixNonBlockConnect(char *err, char *path) 406 | { 407 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK); 408 | } 409 | 410 | /* Like read(2) but make sure 'count' is read before to return 411 | * (unless error or EOF condition is encountered) */ 412 | int anetRead(int fd, char *buf, int count) 413 | { 414 | ssize_t nread, totlen = 0; 415 | while(totlen != count) { 416 | nread = read(fd,buf,count-totlen); 417 | if (nread == 0) return totlen; 418 | if (nread == -1) return -1; 419 | totlen += nread; 420 | buf += nread; 421 | } 422 | return totlen; 423 | } 424 | 425 | /* Like write(2) but make sure 'count' is written before to return 426 | * (unless error is encountered) */ 427 | int anetWrite(int fd, char *buf, int count) 428 | { 429 | ssize_t nwritten, totlen = 0; 430 | while(totlen != count) { 431 | nwritten = write(fd,buf,count-totlen); 432 | if (nwritten == 0) return totlen; 433 | if (nwritten == -1) return -1; 434 | totlen += nwritten; 435 | buf += nwritten; 436 | } 437 | return totlen; 438 | } 439 | 440 | static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) { 441 | if (bind(s,sa,len) == -1) { 442 | anetSetError(err, "bind: %s", strerror(errno)); 443 | close(s); 444 | return ANET_ERR; 445 | } 446 | 447 | if (listen(s, backlog) == -1) { 448 | anetSetError(err, "listen: %s", strerror(errno)); 449 | close(s); 450 | return ANET_ERR; 451 | } 452 | return ANET_OK; 453 | } 454 | 455 | static int anetV6Only(char *err, int s) { 456 | int yes = 1; 457 | if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) { 458 | anetSetError(err, "setsockopt: %s", strerror(errno)); 459 | close(s); 460 | return ANET_ERR; 461 | } 462 | return ANET_OK; 463 | } 464 | 465 | static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog) 466 | { 467 | int s = -1, rv; 468 | char _port[6]; /* strlen("65535") */ 469 | struct addrinfo hints, *servinfo, *p; 470 | 471 | snprintf(_port,6,"%d",port); 472 | memset(&hints,0,sizeof(hints)); 473 | hints.ai_family = af; 474 | hints.ai_socktype = SOCK_STREAM; 475 | hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */ 476 | 477 | if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) { 478 | anetSetError(err, "%s", gai_strerror(rv)); 479 | return ANET_ERR; 480 | } 481 | for (p = servinfo; p != NULL; p = p->ai_next) { 482 | if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) 483 | continue; 484 | 485 | if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error; 486 | if (anetSetReuseAddr(err,s) == ANET_ERR) goto error; 487 | if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR; 488 | goto end; 489 | } 490 | if (p == NULL) { 491 | anetSetError(err, "unable to bind socket, errno: %d", errno); 492 | goto error; 493 | } 494 | 495 | error: 496 | if (s != -1) close(s); 497 | s = ANET_ERR; 498 | end: 499 | freeaddrinfo(servinfo); 500 | return s; 501 | } 502 | 503 | int anetTcpServer(char *err, int port, char *bindaddr, int backlog) 504 | { 505 | return _anetTcpServer(err, port, bindaddr, AF_INET, backlog); 506 | } 507 | 508 | int anetTcp6Server(char *err, int port, char *bindaddr, int backlog) 509 | { 510 | return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog); 511 | } 512 | 513 | int anetUnixServer(char *err, char *path, mode_t perm, int backlog) 514 | { 515 | int s; 516 | struct sockaddr_un sa; 517 | 518 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 519 | return ANET_ERR; 520 | 521 | memset(&sa,0,sizeof(sa)); 522 | sa.sun_family = AF_LOCAL; 523 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 524 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR) 525 | return ANET_ERR; 526 | if (perm) 527 | chmod(sa.sun_path, perm); 528 | return s; 529 | } 530 | 531 | static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { 532 | int fd; 533 | while(1) { 534 | fd = accept(s,sa,len); 535 | if (fd == -1) { 536 | if (errno == EINTR) 537 | continue; 538 | else { 539 | anetSetError(err, "accept: %s", strerror(errno)); 540 | return ANET_ERR; 541 | } 542 | } 543 | break; 544 | } 545 | return fd; 546 | } 547 | 548 | int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) { 549 | int fd; 550 | struct sockaddr_storage sa; 551 | socklen_t salen = sizeof(sa); 552 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == -1) 553 | return ANET_ERR; 554 | 555 | if (sa.ss_family == AF_INET) { 556 | struct sockaddr_in *s = (struct sockaddr_in *)&sa; 557 | if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len); 558 | if (port) *port = ntohs(s->sin_port); 559 | } else { 560 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa; 561 | if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len); 562 | if (port) *port = ntohs(s->sin6_port); 563 | } 564 | return fd; 565 | } 566 | 567 | int anetUnixAccept(char *err, int s) { 568 | int fd; 569 | struct sockaddr_un sa; 570 | socklen_t salen = sizeof(sa); 571 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == -1) 572 | return ANET_ERR; 573 | 574 | return fd; 575 | } 576 | 577 | int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) { 578 | struct sockaddr_storage sa; 579 | socklen_t salen = sizeof(sa); 580 | 581 | if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) goto error; 582 | if (ip_len == 0) goto error; 583 | 584 | if (sa.ss_family == AF_INET) { 585 | struct sockaddr_in *s = (struct sockaddr_in *)&sa; 586 | if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len); 587 | if (port) *port = ntohs(s->sin_port); 588 | } else if (sa.ss_family == AF_INET6) { 589 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa; 590 | if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len); 591 | if (port) *port = ntohs(s->sin6_port); 592 | } else if (sa.ss_family == AF_UNIX) { 593 | if (ip) strncpy(ip,"/unixsocket",ip_len); 594 | if (port) *port = 0; 595 | } else { 596 | goto error; 597 | } 598 | return 0; 599 | 600 | error: 601 | if (ip) { 602 | if (ip_len >= 2) { 603 | ip[0] = '?'; 604 | ip[1] = '\0'; 605 | } else if (ip_len == 1) { 606 | ip[0] = '\0'; 607 | } 608 | } 609 | if (port) *port = 0; 610 | return -1; 611 | } 612 | 613 | /* Format an IP,port pair into something easy to parse. If IP is IPv6 614 | * (matches for ":"), the ip is surrounded by []. IP and port are just 615 | * separated by colons. This the standard to display addresses within Redis. */ 616 | int anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) { 617 | return snprintf(buf,buf_len, strchr(ip,':') ? 618 | "[%s]:%d" : "%s:%d", ip, port); 619 | } 620 | 621 | /* Like anetFormatAddr() but extract ip and port from the socket's peer. */ 622 | int anetFormatPeer(int fd, char *buf, size_t buf_len) { 623 | char ip[INET6_ADDRSTRLEN]; 624 | int port; 625 | 626 | anetPeerToString(fd,ip,sizeof(ip),&port); 627 | return anetFormatAddr(buf, buf_len, ip, port); 628 | } 629 | 630 | int anetSockName(int fd, char *ip, size_t ip_len, int *port) { 631 | struct sockaddr_storage sa; 632 | socklen_t salen = sizeof(sa); 633 | 634 | if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) { 635 | if (port) *port = 0; 636 | ip[0] = '?'; 637 | ip[1] = '\0'; 638 | return -1; 639 | } 640 | if (sa.ss_family == AF_INET) { 641 | struct sockaddr_in *s = (struct sockaddr_in *)&sa; 642 | if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len); 643 | if (port) *port = ntohs(s->sin_port); 644 | } else { 645 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa; 646 | if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len); 647 | if (port) *port = ntohs(s->sin6_port); 648 | } 649 | return 0; 650 | } 651 | 652 | int anetFormatSock(int fd, char *fmt, size_t fmt_len) { 653 | char ip[INET6_ADDRSTRLEN]; 654 | int port; 655 | 656 | anetSockName(fd,ip,sizeof(ip),&port); 657 | return anetFormatAddr(fmt, fmt_len, ip, port); 658 | } 659 | -------------------------------------------------------------------------------- /src/anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #include 35 | 36 | #define ANET_OK 0 37 | #define ANET_ERR -1 38 | #define ANET_ERR_LEN 256 39 | 40 | /* Flags used with certain functions. */ 41 | #define ANET_NONE 0 42 | #define ANET_IP_ONLY (1<<0) 43 | 44 | #if defined(__sun) || defined(_AIX) 45 | #define AF_LOCAL AF_UNIX 46 | #endif 47 | 48 | #ifdef _AIX 49 | #undef ip_len 50 | #endif 51 | 52 | int anetTcpConnect(char *err, char *addr, int port); 53 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 54 | int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr); 55 | int anetTcpNonBlockBestEffortBindConnect(char *err, char *addr, int port, char *source_addr); 56 | int anetUnixConnect(char *err, char *path); 57 | int anetUnixNonBlockConnect(char *err, char *path); 58 | int anetRead(int fd, char *buf, int count); 59 | int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len); 60 | int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len); 61 | int anetTcpServer(char *err, int port, char *bindaddr, int backlog); 62 | int anetTcp6Server(char *err, int port, char *bindaddr, int backlog); 63 | int anetUnixServer(char *err, char *path, mode_t perm, int backlog); 64 | int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port); 65 | int anetUnixAccept(char *err, int serversock); 66 | int anetWrite(int fd, char *buf, int count); 67 | int anetNonBlock(char *err, int fd); 68 | int anetBlock(char *err, int fd); 69 | int anetEnableTcpNoDelay(char *err, int fd); 70 | int anetDisableTcpNoDelay(char *err, int fd); 71 | int anetTcpKeepAlive(char *err, int fd); 72 | int anetSendTimeout(char *err, int fd, long long ms); 73 | int anetPeerToString(int fd, char *ip, size_t ip_len, int *port); 74 | int anetKeepAlive(char *err, int fd, int interval); 75 | int anetSockName(int fd, char *ip, size_t ip_len, int *port); 76 | int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port); 77 | int anetFormatPeer(int fd, char *fmt, size_t fmt_len); 78 | int anetFormatSock(int fd, char *fmt, size_t fmt_len); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __CONFIG_H 31 | #define __CONFIG_H 32 | 33 | #ifdef __APPLE__ 34 | #include 35 | #endif 36 | 37 | /* Test for polling API */ 38 | #ifdef __linux__ 39 | #define HAVE_EPOLL 1 40 | #endif 41 | 42 | #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__) 43 | #define HAVE_KQUEUE 1 44 | #endif 45 | 46 | #ifdef __sun 47 | #include 48 | #ifdef _DTRACE_VERSION 49 | #define HAVE_EVPORT 1 50 | #endif 51 | #endif 52 | 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/fmacros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _REDIS_FMACRO_H 31 | #define _REDIS_FMACRO_H 32 | 33 | #define _BSD_SOURCE 34 | 35 | #if defined(__linux__) 36 | #define _GNU_SOURCE 37 | #define _DEFAULT_SOURCE 38 | #endif 39 | 40 | #if defined(_AIX) 41 | #define _ALL_SOURCE 42 | #endif 43 | 44 | #if defined(__linux__) || defined(__OpenBSD__) 45 | #define _XOPEN_SOURCE 700 46 | /* 47 | * On NetBSD, _XOPEN_SOURCE undefines _NETBSD_SOURCE and 48 | * thus hides inet_aton etc. 49 | */ 50 | #elif !defined(__NetBSD__) 51 | #define _XOPEN_SOURCE 52 | #endif 53 | 54 | #if defined(__sun) 55 | #define _POSIX_C_SOURCE 199506L 56 | #endif 57 | 58 | #define _LARGEFILE_SOURCE 59 | #define _FILE_OFFSET_BITS 64 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/zmalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef _ZMALLOC_H 2 | #define _ZMALLOC_H 3 | 4 | #ifndef zmalloc 5 | #define zmalloc malloc 6 | #endif 7 | 8 | #ifndef zfree 9 | #define zfree free 10 | #endif 11 | 12 | #ifndef zrealloc 13 | #define zrealloc realloc 14 | #endif 15 | 16 | #endif /* _ZMALLOC_H */ 17 | --------------------------------------------------------------------------------