Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

first commit

  • Loading branch information...
commit 860b602c80310ac9d618bae43c63c306ec2f7a03 0 parents
Salvatore Sanfilippo authored September 20, 2010
40  Makefile
... ...
@@ -0,0 +1,40 @@
  1
+# MCbenchmark Makefile
  2
+# Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com>
  3
+# This file is released under the BSD license, see the COPYING file
  4
+
  5
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
  6
+OPTIMIZATION?=-O2
  7
+ifeq ($(uname_S),SunOS)
  8
+  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
  9
+  CCLINK?= -ldl -lnsl -lsocket -lm
  10
+else
  11
+  CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
  12
+  CCLINK?= -lm
  13
+endif
  14
+CCOPT= $(CFLAGS) $(CCLINK) $(ARCH) $(PROF)
  15
+DEBUG?= -g -rdynamic -ggdb 
  16
+
  17
+PREFIX= /usr/local
  18
+INSTALL_BIN= $(PREFIX)/bin
  19
+INSTALL= cp -p
  20
+
  21
+BENCHOBJ = ae.o anet.o mc-benchmark.o sds.o adlist.o zmalloc.o
  22
+
  23
+BENCHPRGNAME = mc-benchmark
  24
+
  25
+all: mc-benchmark
  26
+
  27
+mc-benchmark: $(BENCHOBJ)
  28
+	$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ)
  29
+
  30
+.c.o:
  31
+	$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
  32
+
  33
+clean:
  34
+	rm -rf $(BENCHPRGNAME) *.o
  35
+
  36
+dep:
  37
+	$(CC) -MM *.c
  38
+
  39
+bench:
  40
+	./mc-benchmark
7  README
... ...
@@ -0,0 +1,7 @@
  1
+This is a straightforward port of redis-benchmark to memcache protocol.
  2
+
  3
+This way it is possible to test Redis and Memcache with not just an apple to
  4
+apple comparison, but also using the exactly same mouth... :)
  5
+
  6
+For feedbacks please send a message to the Redis Google Group
  7
+Alternatively drop an email t$ antirez at gmail dot com
325  adlist.c
... ...
@@ -0,0 +1,325 @@
  1
+/* adlist.c - A generic doubly linked list implementation
  2
+ *
  3
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot 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 <stdlib.h>
  33
+#include "adlist.h"
  34
+#include "zmalloc.h"
  35
+
  36
+/* Create a new list. The created list can be freed with
  37
+ * AlFreeList(), but private value of every node need to be freed
  38
+ * by the user before to call AlFreeList().
  39
+ *
  40
+ * On error, NULL is returned. Otherwise the pointer to the new list. */
  41
+list *listCreate(void)
  42
+{
  43
+    struct list *list;
  44
+
  45
+    if ((list = zmalloc(sizeof(*list))) == NULL)
  46
+        return NULL;
  47
+    list->head = list->tail = NULL;
  48
+    list->len = 0;
  49
+    list->dup = NULL;
  50
+    list->free = NULL;
  51
+    list->match = NULL;
  52
+    return list;
  53
+}
  54
+
  55
+/* Free the whole list.
  56
+ *
  57
+ * This function can't fail. */
  58
+void listRelease(list *list)
  59
+{
  60
+    unsigned int len;
  61
+    listNode *current, *next;
  62
+
  63
+    current = list->head;
  64
+    len = list->len;
  65
+    while(len--) {
  66
+        next = current->next;
  67
+        if (list->free) list->free(current->value);
  68
+        zfree(current);
  69
+        current = next;
  70
+    }
  71
+    zfree(list);
  72
+}
  73
+
  74
+/* Add a new node to the list, to head, contaning the specified 'value'
  75
+ * pointer as value.
  76
+ *
  77
+ * On error, NULL is returned and no operation is performed (i.e. the
  78
+ * list remains unaltered).
  79
+ * On success the 'list' pointer you pass to the function is returned. */
  80
+list *listAddNodeHead(list *list, void *value)
  81
+{
  82
+    listNode *node;
  83
+
  84
+    if ((node = zmalloc(sizeof(*node))) == NULL)
  85
+        return NULL;
  86
+    node->value = value;
  87
+    if (list->len == 0) {
  88
+        list->head = list->tail = node;
  89
+        node->prev = node->next = NULL;
  90
+    } else {
  91
+        node->prev = NULL;
  92
+        node->next = list->head;
  93
+        list->head->prev = node;
  94
+        list->head = node;
  95
+    }
  96
+    list->len++;
  97
+    return list;
  98
+}
  99
+
  100
+/* Add a new node to the list, to tail, contaning the specified 'value'
  101
+ * pointer as value.
  102
+ *
  103
+ * On error, NULL is returned and no operation is performed (i.e. the
  104
+ * list remains unaltered).
  105
+ * On success the 'list' pointer you pass to the function is returned. */
  106
+list *listAddNodeTail(list *list, void *value)
  107
+{
  108
+    listNode *node;
  109
+
  110
+    if ((node = zmalloc(sizeof(*node))) == NULL)
  111
+        return NULL;
  112
+    node->value = value;
  113
+    if (list->len == 0) {
  114
+        list->head = list->tail = node;
  115
+        node->prev = node->next = NULL;
  116
+    } else {
  117
+        node->prev = list->tail;
  118
+        node->next = NULL;
  119
+        list->tail->next = node;
  120
+        list->tail = node;
  121
+    }
  122
+    list->len++;
  123
+    return list;
  124
+}
  125
+
  126
+list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
  127
+    listNode *node;
  128
+
  129
+    if ((node = zmalloc(sizeof(*node))) == NULL)
  130
+        return NULL;
  131
+    node->value = value;
  132
+    if (after) {
  133
+        node->prev = old_node;
  134
+        node->next = old_node->next;
  135
+        if (list->tail == old_node) {
  136
+            list->tail = node;
  137
+        }
  138
+    } else {
  139
+        node->next = old_node;
  140
+        node->prev = old_node->prev;
  141
+        if (list->head == old_node) {
  142
+            list->head = node;
  143
+        }
  144
+    }
  145
+    if (node->prev != NULL) {
  146
+        node->prev->next = node;
  147
+    }
  148
+    if (node->next != NULL) {
  149
+        node->next->prev = node;
  150
+    }
  151
+    list->len++;
  152
+    return list;
  153
+}
  154
+
  155
+/* Remove the specified node from the specified list.
  156
+ * It's up to the caller to free the private value of the node.
  157
+ *
  158
+ * This function can't fail. */
  159
+void listDelNode(list *list, listNode *node)
  160
+{
  161
+    if (node->prev)
  162
+        node->prev->next = node->next;
  163
+    else
  164
+        list->head = node->next;
  165
+    if (node->next)
  166
+        node->next->prev = node->prev;
  167
+    else
  168
+        list->tail = node->prev;
  169
+    if (list->free) list->free(node->value);
  170
+    zfree(node);
  171
+    list->len--;
  172
+}
  173
+
  174
+/* Returns a list iterator 'iter'. After the initialization every
  175
+ * call to listNext() will return the next element of the list.
  176
+ *
  177
+ * This function can't fail. */
  178
+listIter *listGetIterator(list *list, int direction)
  179
+{
  180
+    listIter *iter;
  181
+    
  182
+    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
  183
+    if (direction == AL_START_HEAD)
  184
+        iter->next = list->head;
  185
+    else
  186
+        iter->next = list->tail;
  187
+    iter->direction = direction;
  188
+    return iter;
  189
+}
  190
+
  191
+/* Release the iterator memory */
  192
+void listReleaseIterator(listIter *iter) {
  193
+    zfree(iter);
  194
+}
  195
+
  196
+/* Create an iterator in the list private iterator structure */
  197
+void listRewind(list *list, listIter *li) {
  198
+    li->next = list->head;
  199
+    li->direction = AL_START_HEAD;
  200
+}
  201
+
  202
+void listRewindTail(list *list, listIter *li) {
  203
+    li->next = list->tail;
  204
+    li->direction = AL_START_TAIL;
  205
+}
  206
+
  207
+/* Return the next element of an iterator.
  208
+ * It's valid to remove the currently returned element using
  209
+ * listDelNode(), but not to remove other elements.
  210
+ *
  211
+ * The function returns a pointer to the next element of the list,
  212
+ * or NULL if there are no more elements, so the classical usage patter
  213
+ * is:
  214
+ *
  215
+ * iter = listGetIterator(list,<direction>);
  216
+ * while ((node = listNext(iter)) != NULL) {
  217
+ *     doSomethingWith(listNodeValue(node));
  218
+ * }
  219
+ *
  220
+ * */
  221
+listNode *listNext(listIter *iter)
  222
+{
  223
+    listNode *current = iter->next;
  224
+
  225
+    if (current != NULL) {
  226
+        if (iter->direction == AL_START_HEAD)
  227
+            iter->next = current->next;
  228
+        else
  229
+            iter->next = current->prev;
  230
+    }
  231
+    return current;
  232
+}
  233
+
  234
+/* Duplicate the whole list. On out of memory NULL is returned.
  235
+ * On success a copy of the original list is returned.
  236
+ *
  237
+ * The 'Dup' method set with listSetDupMethod() function is used
  238
+ * to copy the node value. Otherwise the same pointer value of
  239
+ * the original node is used as value of the copied node.
  240
+ *
  241
+ * The original list both on success or error is never modified. */
  242
+list *listDup(list *orig)
  243
+{
  244
+    list *copy;
  245
+    listIter *iter;
  246
+    listNode *node;
  247
+
  248
+    if ((copy = listCreate()) == NULL)
  249
+        return NULL;
  250
+    copy->dup = orig->dup;
  251
+    copy->free = orig->free;
  252
+    copy->match = orig->match;
  253
+    iter = listGetIterator(orig, AL_START_HEAD);
  254
+    while((node = listNext(iter)) != NULL) {
  255
+        void *value;
  256
+
  257
+        if (copy->dup) {
  258
+            value = copy->dup(node->value);
  259
+            if (value == NULL) {
  260
+                listRelease(copy);
  261
+                listReleaseIterator(iter);
  262
+                return NULL;
  263
+            }
  264
+        } else
  265
+            value = node->value;
  266
+        if (listAddNodeTail(copy, value) == NULL) {
  267
+            listRelease(copy);
  268
+            listReleaseIterator(iter);
  269
+            return NULL;
  270
+        }
  271
+    }
  272
+    listReleaseIterator(iter);
  273
+    return copy;
  274
+}
  275
+
  276
+/* Search the list for a node matching a given key.
  277
+ * The match is performed using the 'match' method
  278
+ * set with listSetMatchMethod(). If no 'match' method
  279
+ * is set, the 'value' pointer of every node is directly
  280
+ * compared with the 'key' pointer.
  281
+ *
  282
+ * On success the first matching node pointer is returned
  283
+ * (search starts from head). If no matching node exists
  284
+ * NULL is returned. */
  285
+listNode *listSearchKey(list *list, void *key)
  286
+{
  287
+    listIter *iter;
  288
+    listNode *node;
  289
+
  290
+    iter = listGetIterator(list, AL_START_HEAD);
  291
+    while((node = listNext(iter)) != NULL) {
  292
+        if (list->match) {
  293
+            if (list->match(node->value, key)) {
  294
+                listReleaseIterator(iter);
  295
+                return node;
  296
+            }
  297
+        } else {
  298
+            if (key == node->value) {
  299
+                listReleaseIterator(iter);
  300
+                return node;
  301
+            }
  302
+        }
  303
+    }
  304
+    listReleaseIterator(iter);
  305
+    return NULL;
  306
+}
  307
+
  308
+/* Return the element at the specified zero-based index
  309
+ * where 0 is the head, 1 is the element next to head
  310
+ * and so on. Negative integers are used in order to count
  311
+ * from the tail, -1 is the last element, -2 the penultimante
  312
+ * and so on. If the index is out of range NULL is returned. */
  313
+listNode *listIndex(list *list, int index) {
  314
+    listNode *n;
  315
+
  316
+    if (index < 0) {
  317
+        index = (-index)-1;
  318
+        n = list->tail;
  319
+        while(index-- && n) n = n->prev;
  320
+    } else {
  321
+        n = list->head;
  322
+        while(index-- && n) n = n->next;
  323
+    }
  324
+    return n;
  325
+}
92  adlist.h
... ...
@@ -0,0 +1,92 @@
  1
+/* adlist.h - A generic doubly linked list implementation
  2
+ *
  3
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot 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
+#ifndef __ADLIST_H__
  32
+#define __ADLIST_H__
  33
+
  34
+/* Node, List, and Iterator are the only data structures used currently. */
  35
+
  36
+typedef struct listNode {
  37
+    struct listNode *prev;
  38
+    struct listNode *next;
  39
+    void *value;
  40
+} listNode;
  41
+
  42
+typedef struct listIter {
  43
+    listNode *next;
  44
+    int direction;
  45
+} listIter;
  46
+
  47
+typedef struct list {
  48
+    listNode *head;
  49
+    listNode *tail;
  50
+    void *(*dup)(void *ptr);
  51
+    void (*free)(void *ptr);
  52
+    int (*match)(void *ptr, void *key);
  53
+    unsigned int len;
  54
+} list;
  55
+
  56
+/* Functions implemented as macros */
  57
+#define listLength(l) ((l)->len)
  58
+#define listFirst(l) ((l)->head)
  59
+#define listLast(l) ((l)->tail)
  60
+#define listPrevNode(n) ((n)->prev)
  61
+#define listNextNode(n) ((n)->next)
  62
+#define listNodeValue(n) ((n)->value)
  63
+
  64
+#define listSetDupMethod(l,m) ((l)->dup = (m))
  65
+#define listSetFreeMethod(l,m) ((l)->free = (m))
  66
+#define listSetMatchMethod(l,m) ((l)->match = (m))
  67
+
  68
+#define listGetDupMethod(l) ((l)->dup)
  69
+#define listGetFree(l) ((l)->free)
  70
+#define listGetMatchMethod(l) ((l)->match)
  71
+
  72
+/* Prototypes */
  73
+list *listCreate(void);
  74
+void listRelease(list *list);
  75
+list *listAddNodeHead(list *list, void *value);
  76
+list *listAddNodeTail(list *list, void *value);
  77
+list *listInsertNode(list *list, listNode *old_node, void *value, int after);
  78
+void listDelNode(list *list, listNode *node);
  79
+listIter *listGetIterator(list *list, int direction);
  80
+listNode *listNext(listIter *iter);
  81
+void listReleaseIterator(listIter *iter);
  82
+list *listDup(list *orig);
  83
+listNode *listSearchKey(list *list, void *key);
  84
+listNode *listIndex(list *list, int index);
  85
+void listRewind(list *list, listIter *li);
  86
+void listRewindTail(list *list, listIter *li);
  87
+
  88
+/* Directions for iterators */
  89
+#define AL_START_HEAD 0
  90
+#define AL_START_TAIL 1
  91
+
  92
+#endif /* __ADLIST_H__ */
390  ae.c
... ...
@@ -0,0 +1,390 @@
  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 <antirez at gmail dot com>
  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 <stdio.h>
  34
+#include <sys/time.h>
  35
+#include <sys/types.h>
  36
+#include <unistd.h>
  37
+#include <stdlib.h>
  38
+
  39
+#include "ae.h"
  40
+#include "zmalloc.h"
  41
+#include "config.h"
  42
+
  43
+/* Include the best multiplexing layer supported by this system.
  44
+ * The following should be ordered by performances, descending. */
  45
+#ifdef HAVE_EPOLL
  46
+#include "ae_epoll.c"
  47
+#else
  48
+    #ifdef HAVE_KQUEUE
  49
+    #include "ae_kqueue.c"
  50
+    #else
  51
+    #include "ae_select.c"
  52
+    #endif
  53
+#endif
  54
+
  55
+aeEventLoop *aeCreateEventLoop(void) {
  56
+    aeEventLoop *eventLoop;
  57
+    int i;
  58
+
  59
+    eventLoop = zmalloc(sizeof(*eventLoop));
  60
+    if (!eventLoop) return NULL;
  61
+    eventLoop->timeEventHead = NULL;
  62
+    eventLoop->timeEventNextId = 0;
  63
+    eventLoop->stop = 0;
  64
+    eventLoop->maxfd = -1;
  65
+    eventLoop->beforesleep = NULL;
  66
+    if (aeApiCreate(eventLoop) == -1) {
  67
+        zfree(eventLoop);
  68
+        return NULL;
  69
+    }
  70
+    /* Events with mask == AE_NONE are not set. So let's initialize the
  71
+     * vector with it. */
  72
+    for (i = 0; i < AE_SETSIZE; i++)
  73
+        eventLoop->events[i].mask = AE_NONE;
  74
+    return eventLoop;
  75
+}
  76
+
  77
+void aeDeleteEventLoop(aeEventLoop *eventLoop) {
  78
+    aeApiFree(eventLoop);
  79
+    zfree(eventLoop);
  80
+}
  81
+
  82
+void aeStop(aeEventLoop *eventLoop) {
  83
+    eventLoop->stop = 1;
  84
+}
  85
+
  86
+int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
  87
+        aeFileProc *proc, void *clientData)
  88
+{
  89
+    if (fd >= AE_SETSIZE) return AE_ERR;
  90
+    aeFileEvent *fe = &eventLoop->events[fd];
  91
+
  92
+    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
  93
+        return AE_ERR;
  94
+    fe->mask |= mask;
  95
+    if (mask & AE_READABLE) fe->rfileProc = proc;
  96
+    if (mask & AE_WRITABLE) fe->wfileProc = proc;
  97
+    fe->clientData = clientData;
  98
+    if (fd > eventLoop->maxfd)
  99
+        eventLoop->maxfd = fd;
  100
+    return AE_OK;
  101
+}
  102
+
  103
+void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
  104
+{
  105
+    if (fd >= AE_SETSIZE) return;
  106
+    aeFileEvent *fe = &eventLoop->events[fd];
  107
+
  108
+    if (fe->mask == AE_NONE) return;
  109
+    fe->mask = fe->mask & (~mask);
  110
+    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
  111
+        /* Update the max fd */
  112
+        int j;
  113
+
  114
+        for (j = eventLoop->maxfd-1; j >= 0; j--)
  115
+            if (eventLoop->events[j].mask != AE_NONE) break;
  116
+        eventLoop->maxfd = j;
  117
+    }
  118
+    aeApiDelEvent(eventLoop, fd, mask);
  119
+}
  120
+
  121
+static void aeGetTime(long *seconds, long *milliseconds)
  122
+{
  123
+    struct timeval tv;
  124
+
  125
+    gettimeofday(&tv, NULL);
  126
+    *seconds = tv.tv_sec;
  127
+    *milliseconds = tv.tv_usec/1000;
  128
+}
  129
+
  130
+static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
  131
+    long cur_sec, cur_ms, when_sec, when_ms;
  132
+
  133
+    aeGetTime(&cur_sec, &cur_ms);
  134
+    when_sec = cur_sec + milliseconds/1000;
  135
+    when_ms = cur_ms + milliseconds%1000;
  136
+    if (when_ms >= 1000) {
  137
+        when_sec ++;
  138
+        when_ms -= 1000;
  139
+    }
  140
+    *sec = when_sec;
  141
+    *ms = when_ms;
  142
+}
  143
+
  144
+long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
  145
+        aeTimeProc *proc, void *clientData,
  146
+        aeEventFinalizerProc *finalizerProc)
  147
+{
  148
+    long long id = eventLoop->timeEventNextId++;
  149
+    aeTimeEvent *te;
  150
+
  151
+    te = zmalloc(sizeof(*te));
  152
+    if (te == NULL) return AE_ERR;
  153
+    te->id = id;
  154
+    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
  155
+    te->timeProc = proc;
  156
+    te->finalizerProc = finalizerProc;
  157
+    te->clientData = clientData;
  158
+    te->next = eventLoop->timeEventHead;
  159
+    eventLoop->timeEventHead = te;
  160
+    return id;
  161
+}
  162
+
  163
+int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
  164
+{
  165
+    aeTimeEvent *te, *prev = NULL;
  166
+
  167
+    te = eventLoop->timeEventHead;
  168
+    while(te) {
  169
+        if (te->id == id) {
  170
+            if (prev == NULL)
  171
+                eventLoop->timeEventHead = te->next;
  172
+            else
  173
+                prev->next = te->next;
  174
+            if (te->finalizerProc)
  175
+                te->finalizerProc(eventLoop, te->clientData);
  176
+            zfree(te);
  177
+            return AE_OK;
  178
+        }
  179
+        prev = te;
  180
+        te = te->next;
  181
+    }
  182
+    return AE_ERR; /* NO event with the specified ID found */
  183
+}
  184
+
  185
+/* Search the first timer to fire.
  186
+ * This operation is useful to know how many time the select can be
  187
+ * put in sleep without to delay any event.
  188
+ * If there are no timers NULL is returned.
  189
+ *
  190
+ * Note that's O(N) since time events are unsorted.
  191
+ * Possible optimizations (not needed by Redis so far, but...):
  192
+ * 1) Insert the event in order, so that the nearest is just the head.
  193
+ *    Much better but still insertion or deletion of timers is O(N).
  194
+ * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
  195
+ */
  196
+static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
  197
+{
  198
+    aeTimeEvent *te = eventLoop->timeEventHead;
  199
+    aeTimeEvent *nearest = NULL;
  200
+
  201
+    while(te) {
  202
+        if (!nearest || te->when_sec < nearest->when_sec ||
  203
+                (te->when_sec == nearest->when_sec &&
  204
+                 te->when_ms < nearest->when_ms))
  205
+            nearest = te;
  206
+        te = te->next;
  207
+    }
  208
+    return nearest;
  209
+}
  210
+
  211
+/* Process time events */
  212
+static int processTimeEvents(aeEventLoop *eventLoop) {
  213
+    int processed = 0;
  214
+    aeTimeEvent *te;
  215
+    long long maxId;
  216
+
  217
+    te = eventLoop->timeEventHead;
  218
+    maxId = eventLoop->timeEventNextId-1;
  219
+    while(te) {
  220
+        long now_sec, now_ms;
  221
+        long long id;
  222
+
  223
+        if (te->id > maxId) {
  224
+            te = te->next;
  225
+            continue;
  226
+        }
  227
+        aeGetTime(&now_sec, &now_ms);
  228
+        if (now_sec > te->when_sec ||
  229
+            (now_sec == te->when_sec && now_ms >= te->when_ms))
  230
+        {
  231
+            int retval;
  232
+
  233
+            id = te->id;
  234
+            retval = te->timeProc(eventLoop, id, te->clientData);
  235
+            processed++;
  236
+            /* After an event is processed our time event list may
  237
+             * no longer be the same, so we restart from head.
  238
+             * Still we make sure to don't process events registered
  239
+             * by event handlers itself in order to don't loop forever.
  240
+             * To do so we saved the max ID we want to handle.
  241
+             *
  242
+             * FUTURE OPTIMIZATIONS:
  243
+             * Note that this is NOT great algorithmically. Redis uses
  244
+             * a single time event so it's not a problem but the right
  245
+             * way to do this is to add the new elements on head, and
  246
+             * to flag deleted elements in a special way for later
  247
+             * deletion (putting references to the nodes to delete into
  248
+             * another linked list). */
  249
+            if (retval != AE_NOMORE) {
  250
+                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
  251
+            } else {
  252
+                aeDeleteTimeEvent(eventLoop, id);
  253
+            }
  254
+            te = eventLoop->timeEventHead;
  255
+        } else {
  256
+            te = te->next;
  257
+        }
  258
+    }
  259
+    return processed;
  260
+}
  261
+
  262
+/* Process every pending time event, then every pending file event
  263
+ * (that may be registered by time event callbacks just processed).
  264
+ * Without special flags the function sleeps until some file event
  265
+ * fires, or when the next time event occurrs (if any).
  266
+ *
  267
+ * If flags is 0, the function does nothing and returns.
  268
+ * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
  269
+ * if flags has AE_FILE_EVENTS set, file events are processed.
  270
+ * if flags has AE_TIME_EVENTS set, time events are processed.
  271
+ * if flags has AE_DONT_WAIT set the function returns ASAP until all
  272
+ * the events that's possible to process without to wait are processed.
  273
+ *
  274
+ * The function returns the number of events processed. */
  275
+int aeProcessEvents(aeEventLoop *eventLoop, int flags)
  276
+{
  277
+    int processed = 0, numevents;
  278
+
  279
+    /* Nothing to do? return ASAP */
  280
+    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
  281
+
  282
+    /* Note that we want call select() even if there are no
  283
+     * file events to process as long as we want to process time
  284
+     * events, in order to sleep until the next time event is ready
  285
+     * to fire. */
  286
+    if (eventLoop->maxfd != -1 ||
  287
+        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
  288
+        int j;
  289
+        aeTimeEvent *shortest = NULL;
  290
+        struct timeval tv, *tvp;
  291
+
  292
+        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
  293
+            shortest = aeSearchNearestTimer(eventLoop);
  294
+        if (shortest) {
  295
+            long now_sec, now_ms;
  296
+
  297
+            /* Calculate the time missing for the nearest
  298
+             * timer to fire. */
  299
+            aeGetTime(&now_sec, &now_ms);
  300
+            tvp = &tv;
  301
+            tvp->tv_sec = shortest->when_sec - now_sec;
  302
+            if (shortest->when_ms < now_ms) {
  303
+                tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
  304
+                tvp->tv_sec --;
  305
+            } else {
  306
+                tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
  307
+            }
  308
+            if (tvp->tv_sec < 0) tvp->tv_sec = 0;
  309
+            if (tvp->tv_usec < 0) tvp->tv_usec = 0;
  310
+        } else {
  311
+            /* If we have to check for events but need to return
  312
+             * ASAP because of AE_DONT_WAIT we need to se the timeout
  313
+             * to zero */
  314
+            if (flags & AE_DONT_WAIT) {
  315
+                tv.tv_sec = tv.tv_usec = 0;
  316
+                tvp = &tv;
  317
+            } else {
  318
+                /* Otherwise we can block */
  319
+                tvp = NULL; /* wait forever */
  320
+            }
  321
+        }
  322
+
  323
+        numevents = aeApiPoll(eventLoop, tvp);
  324
+        for (j = 0; j < numevents; j++) {
  325
+            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
  326
+            int mask = eventLoop->fired[j].mask;
  327
+            int fd = eventLoop->fired[j].fd;
  328
+            int rfired = 0;
  329
+
  330
+	    /* note the fe->mask & mask & ... code: maybe an already processed
  331
+             * event removed an element that fired and we still didn't
  332
+             * processed, so we check if the event is still valid. */
  333
+            if (fe->mask & mask & AE_READABLE) {
  334
+                rfired = 1;
  335
+                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
  336
+            }
  337
+            if (fe->mask & mask & AE_WRITABLE) {
  338
+                if (!rfired || fe->wfileProc != fe->rfileProc)
  339
+                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
  340
+            }
  341
+            processed++;
  342
+        }
  343
+    }
  344
+    /* Check time events */
  345
+    if (flags & AE_TIME_EVENTS)
  346
+        processed += processTimeEvents(eventLoop);
  347
+
  348
+    return processed; /* return the number of processed file/time events */
  349
+}
  350
+
  351
+/* Wait for millseconds until the given file descriptor becomes
  352
+ * writable/readable/exception */
  353
+int aeWait(int fd, int mask, long long milliseconds) {
  354
+    struct timeval tv;
  355
+    fd_set rfds, wfds, efds;
  356
+    int retmask = 0, retval;
  357
+
  358
+    tv.tv_sec = milliseconds/1000;
  359
+    tv.tv_usec = (milliseconds%1000)*1000;
  360
+    FD_ZERO(&rfds);
  361
+    FD_ZERO(&wfds);
  362
+    FD_ZERO(&efds);
  363
+
  364
+    if (mask & AE_READABLE) FD_SET(fd,&rfds);
  365
+    if (mask & AE_WRITABLE) FD_SET(fd,&wfds);
  366
+    if ((retval = select(fd+1, &rfds, &wfds, &efds, &tv)) > 0) {
  367
+        if (FD_ISSET(fd,&rfds)) retmask |= AE_READABLE;
  368
+        if (FD_ISSET(fd,&wfds)) retmask |= AE_WRITABLE;
  369
+        return retmask;
  370
+    } else {
  371
+        return retval;
  372
+    }
  373
+}
  374
+
  375
+void aeMain(aeEventLoop *eventLoop) {
  376
+    eventLoop->stop = 0;
  377
+    while (!eventLoop->stop) {
  378
+        if (eventLoop->beforesleep != NULL)
  379
+            eventLoop->beforesleep(eventLoop);
  380
+        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
  381
+    }
  382
+}
  383
+
  384
+char *aeGetApiName(void) {
  385
+    return aeApiName();
  386
+}
  387
+
  388
+void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
  389
+    eventLoop->beforesleep = beforesleep;
  390
+}
117  ae.h
... ...
@@ -0,0 +1,117 @@
  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 <antirez at gmail dot com>
  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
+#define AE_SETSIZE (1024*10)    /* Max number of fd supported */
  37
+
  38
+#define AE_OK 0
  39
+#define AE_ERR -1
  40
+
  41
+#define AE_NONE 0
  42
+#define AE_READABLE 1
  43
+#define AE_WRITABLE 2
  44
+
  45
+#define AE_FILE_EVENTS 1
  46
+#define AE_TIME_EVENTS 2
  47
+#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)
  48
+#define AE_DONT_WAIT 4
  49
+
  50
+#define AE_NOMORE -1
  51
+
  52
+/* Macros */
  53
+#define AE_NOTUSED(V) ((void) V)
  54
+
  55
+struct aeEventLoop;
  56
+
  57
+/* Types and data structures */
  58
+typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
  59
+typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
  60
+typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
  61
+typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
  62
+
  63
+/* File event structure */
  64
+typedef struct aeFileEvent {
  65
+    int mask; /* one of AE_(READABLE|WRITABLE) */
  66
+    aeFileProc *rfileProc;
  67
+    aeFileProc *wfileProc;
  68
+    void *clientData;
  69
+} aeFileEvent;
  70
+
  71
+/* Time event structure */
  72
+typedef struct aeTimeEvent {
  73
+    long long id; /* time event identifier. */
  74
+    long when_sec; /* seconds */
  75
+    long when_ms; /* milliseconds */
  76
+    aeTimeProc *timeProc;
  77
+    aeEventFinalizerProc *finalizerProc;
  78
+    void *clientData;
  79
+    struct aeTimeEvent *next;
  80
+} aeTimeEvent;
  81
+
  82
+/* A fired event */
  83
+typedef struct aeFiredEvent {
  84
+    int fd;
  85
+    int mask;
  86
+} aeFiredEvent;
  87
+
  88
+/* State of an event based program */
  89
+typedef struct aeEventLoop {
  90
+    int maxfd;
  91
+    long long timeEventNextId;
  92
+    aeFileEvent events[AE_SETSIZE]; /* Registered events */
  93
+    aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
  94
+    aeTimeEvent *timeEventHead;
  95
+    int stop;
  96
+    void *apidata; /* This is used for polling API specific data */
  97
+    aeBeforeSleepProc *beforesleep;
  98
+} aeEventLoop;
  99
+
  100
+/* Prototypes */
  101
+aeEventLoop *aeCreateEventLoop(void);
  102
+void aeDeleteEventLoop(aeEventLoop *eventLoop);
  103
+void aeStop(aeEventLoop *eventLoop);
  104
+int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
  105
+        aeFileProc *proc, void *clientData);
  106
+void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
  107
+long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
  108
+        aeTimeProc *proc, void *clientData,
  109
+        aeEventFinalizerProc *finalizerProc);
  110
+int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);
  111
+int aeProcessEvents(aeEventLoop *eventLoop, int flags);
  112
+int aeWait(int fd, int mask, long long milliseconds);
  113
+void aeMain(aeEventLoop *eventLoop);
  114
+char *aeGetApiName(void);
  115
+void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
  116
+
  117
+#endif
91  ae_epoll.c
... ...
@@ -0,0 +1,91 @@
  1
+/* Linux epoll(2) based ae.c module
  2
+ * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com
  3
+ * Released under the BSD license. See the COPYING file for more info. */
  4
+
  5
+#include <sys/epoll.h>
  6
+
  7
+typedef struct aeApiState {
  8
+    int epfd;
  9
+    struct epoll_event events[AE_SETSIZE];
  10
+} aeApiState;
  11
+
  12
+static int aeApiCreate(aeEventLoop *eventLoop) {
  13
+    aeApiState *state = zmalloc(sizeof(aeApiState));
  14
+
  15
+    if (!state) return -1;
  16
+    state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */
  17
+    if (state->epfd == -1) return -1;
  18
+    eventLoop->apidata = state;
  19
+    return 0;
  20
+}
  21
+
  22
+static void aeApiFree(aeEventLoop *eventLoop) {
  23
+    aeApiState *state = eventLoop->apidata;
  24
+
  25
+    close(state->epfd);
  26
+    zfree(state);
  27
+}
  28
+
  29
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
  30
+    aeApiState *state = eventLoop->apidata;
  31
+    struct epoll_event ee;
  32
+    /* If the fd was already monitored for some event, we need a MOD
  33
+     * operation. Otherwise we need an ADD operation. */
  34
+    int op = eventLoop->events[fd].mask == AE_NONE ?
  35
+            EPOLL_CTL_ADD : EPOLL_CTL_MOD;
  36
+
  37
+    ee.events = 0;
  38
+    mask |= eventLoop->events[fd].mask; /* Merge old events */
  39
+    if (mask & AE_READABLE) ee.events |= EPOLLIN;
  40
+    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
  41
+    ee.data.u64 = 0; /* avoid valgrind warning */
  42
+    ee.data.fd = fd;
  43
+    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
  44
+    return 0;
  45
+}
  46
+
  47
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
  48
+    aeApiState *state = eventLoop->apidata;
  49
+    struct epoll_event ee;
  50
+    int mask = eventLoop->events[fd].mask & (~delmask);
  51
+
  52
+    ee.events = 0;
  53
+    if (mask & AE_READABLE) ee.events |= EPOLLIN;
  54
+    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
  55
+    ee.data.u64 = 0; /* avoid valgrind warning */
  56
+    ee.data.fd = fd;
  57
+    if (mask != AE_NONE) {
  58
+        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);
  59
+    } else {
  60
+        /* Note, Kernel < 2.6.9 requires a non null event pointer even for
  61
+         * EPOLL_CTL_DEL. */
  62
+        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);
  63
+    }
  64
+}
  65
+
  66
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
  67
+    aeApiState *state = eventLoop->apidata;
  68
+    int retval, numevents = 0;
  69
+
  70
+    retval = epoll_wait(state->epfd,state->events,AE_SETSIZE,
  71
+            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
  72
+    if (retval > 0) {
  73
+        int j;
  74
+
  75
+        numevents = retval;
  76
+        for (j = 0; j < numevents; j++) {
  77
+            int mask = 0;
  78
+            struct epoll_event *e = state->events+j;
  79
+
  80
+            if (e->events & EPOLLIN) mask |= AE_READABLE;
  81
+            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
  82
+            eventLoop->fired[j].fd = e->data.fd;
  83
+            eventLoop->fired[j].mask = mask;
  84
+        }
  85
+    }
  86
+    return numevents;
  87
+}
  88
+
  89
+static char *aeApiName(void) {
  90
+    return "epoll";
  91
+}
93  ae_kqueue.c
... ...
@@ -0,0 +1,93 @@
  1
+/* Kqueue(2)-based ae.c module
  2
+ * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com
  3
+ * Released under the BSD license. See the COPYING file for more info. */
  4
+
  5
+#include <sys/types.h>
  6
+#include <sys/event.h>
  7
+#include <sys/time.h>
  8
+
  9
+typedef struct aeApiState {
  10
+    int kqfd;
  11
+    struct kevent events[AE_SETSIZE];
  12
+} aeApiState;
  13
+
  14
+static int aeApiCreate(aeEventLoop *eventLoop) {
  15
+    aeApiState *state = zmalloc(sizeof(aeApiState));
  16
+
  17
+    if (!state) return -1;
  18
+    state->kqfd = kqueue();
  19
+    if (state->kqfd == -1) return -1;
  20
+    eventLoop->apidata = state;
  21
+    
  22
+    return 0;    
  23
+}
  24
+
  25
+static void aeApiFree(aeEventLoop *eventLoop) {
  26
+    aeApiState *state = eventLoop->apidata;
  27
+
  28
+    close(state->kqfd);
  29
+    zfree(state);
  30
+}
  31
+
  32
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
  33
+    aeApiState *state = eventLoop->apidata;
  34
+    struct kevent ke;
  35
+    
  36
+    if (mask & AE_READABLE) {
  37
+        EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
  38
+        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
  39
+    }
  40
+    if (mask & AE_WRITABLE) {
  41
+        EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
  42
+        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
  43
+    }
  44
+    return 0;
  45
+}
  46
+
  47
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
  48
+    aeApiState *state = eventLoop->apidata;
  49
+    struct kevent ke;
  50
+
  51
+    if (mask & AE_READABLE) {
  52
+        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
  53
+        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
  54
+    }
  55
+    if (mask & AE_WRITABLE) {
  56
+        EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
  57
+        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
  58
+    }
  59
+}
  60
+
  61
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
  62
+    aeApiState *state = eventLoop->apidata;
  63
+    int retval, numevents = 0;
  64
+
  65
+    if (tvp != NULL) {
  66
+        struct timespec timeout;
  67
+        timeout.tv_sec = tvp->tv_sec;
  68
+        timeout.tv_nsec = tvp->tv_usec * 1000;
  69
+        retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, &timeout);
  70
+    } else {
  71
+        retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, NULL);
  72
+    }    
  73
+
  74
+    if (retval > 0) {
  75
+        int j;
  76
+        
  77
+        numevents = retval;
  78
+        for(j = 0; j < numevents; j++) {
  79
+            int mask = 0;
  80
+            struct kevent *e = state->events+j;
  81
+            
  82
+            if (e->filter == EVFILT_READ) mask |= AE_READABLE;
  83
+            if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;
  84
+            eventLoop->fired[j].fd = e->ident; 
  85
+            eventLoop->fired[j].mask = mask;           
  86
+        }
  87
+    }
  88
+    return numevents;
  89
+}
  90
+
  91
+static char *aeApiName(void) {
  92
+    return "kqueue";
  93
+}
72  ae_select.c
... ...
@@ -0,0 +1,72 @@
  1
+/* Select()-based ae.c module
  2
+ * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com
  3
+ * Released under the BSD license. See the COPYING file for more info. */
  4
+
  5
+#include <string.h>
  6
+
  7
+typedef struct aeApiState {
  8
+    fd_set rfds, wfds;
  9
+    /* We need to have a copy of the fd sets as it's not safe to reuse
  10
+     * FD sets after select(). */
  11
+    fd_set _rfds, _wfds;
  12
+} aeApiState;
  13
+
  14
+static int aeApiCreate(aeEventLoop *eventLoop) {
  15
+    aeApiState *state = zmalloc(sizeof(aeApiState));
  16
+
  17
+    if (!state) return -1;
  18
+    FD_ZERO(&state->rfds);
  19
+    FD_ZERO(&state->wfds);
  20
+    eventLoop->apidata = state;
  21
+    return 0;
  22
+}
  23
+
  24
+static void aeApiFree(aeEventLoop *eventLoop) {
  25
+    zfree(eventLoop->apidata);
  26
+}
  27
+
  28
+static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
  29
+    aeApiState *state = eventLoop->apidata;
  30
+
  31
+    if (mask & AE_READABLE) FD_SET(fd,&state->rfds);
  32
+    if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds);
  33
+    return 0;
  34
+}
  35
+
  36
+static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
  37
+    aeApiState *state = eventLoop->apidata;
  38
+
  39
+    if (mask & AE_READABLE) FD_CLR(fd,&state->rfds);
  40
+    if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds);
  41
+}
  42
+
  43
+static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
  44
+    aeApiState *state = eventLoop->apidata;
  45
+    int retval, j, numevents = 0;
  46
+
  47
+    memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));
  48
+    memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));
  49
+
  50
+    retval = select(eventLoop->maxfd+1,
  51
+                &state->_rfds,&state->_wfds,NULL,tvp);
  52
+    if (retval > 0) {
  53
+        for (j = 0; j <= eventLoop->maxfd; j++) {
  54
+            int mask = 0;
  55
+            aeFileEvent *fe = &eventLoop->events[j];
  56
+
  57
+            if (fe->mask == AE_NONE) continue;
  58
+            if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))
  59
+                mask |= AE_READABLE;
  60
+            if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))
  61
+                mask |= AE_WRITABLE;
  62
+            eventLoop->fired[numevents].fd = j;
  63
+            eventLoop->fired[numevents].mask = mask;
  64
+            numevents++;
  65
+        }
  66
+    }
  67
+    return numevents;
  68
+}
  69
+
  70
+static char *aeApiName(void) {
  71
+    return "select";
  72
+}
270  anet.c
... ...
@@ -0,0 +1,270 @@
  1
+/* anet.c -- Basic TCP socket stuff made a bit less boring