├── .gitignore ├── README.md ├── src ├── Makefile ├── cheetah │ ├── config.h │ ├── event.h │ ├── event_ht.h │ ├── includes.h │ ├── list.h │ ├── lock.h │ ├── log.h │ ├── polling_policy.h │ ├── reactor.h │ ├── signal.h │ ├── timer.h │ └── utility.h ├── configure ├── event.c ├── event_ht.c ├── list.c ├── lock.c ├── log.c ├── log_test.c ├── polling_epoll.c ├── polling_kqueue.c ├── polling_policy.c ├── polling_poll.c ├── polling_select.c ├── reactor.c ├── signal.c ├── test_list.c ├── timer.c └── utility.c └── test ├── benchmark.c ├── io_test.c ├── signal_test.c └── timer_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | .tag* 2 | *.[oa] 3 | *.DS_Store 4 | *.so 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A lightweight event-driven library based on reactor pattern in development.
2 | Features already supported:
3 | Regular file io events.
4 | Socket io events.
5 | Signal events.
6 | Timer events.
7 | Features in development:
8 | Asynchronous io events.
9 | To test:
10 | ./configure
11 | make
12 | ./benchmark 13 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC= gcc 2 | CFLAGS= -g -Wall -Werror -fpic 3 | INCLUDE= cheetah 4 | OBJECTS= reactor.o event.o event_ht.o list.o lock.o log.o polling_policy.o signal.o timer.o utility.o 5 | PROGRAM= libcheetah.so 6 | LIBS= -lpthread 7 | LINKER_FLAGS= -fpic -shared 8 | #bench.o: 9 | # $(CC) $(CFLAGS) -c bench.c 10 | 11 | all: $(OBJECTS) 12 | $(CC) $(CFLAGS) $(LINKER_FLAGS) -o $(PROGRAM) $(OBJECTS) 13 | 14 | event.o: $(INCLUDE)/event.h event.c 15 | $(CC) $(CFLAGS) -c event.c 16 | 17 | event_ht.o: $(INCLUDE)/event_ht.h event_ht.c 18 | $(CC) $(CFLAGS) -c event_ht.c 19 | 20 | list.o: $(INCLUDE)/list.h list.c 21 | $(CC) $(CFLAGS) -c list.c 22 | 23 | lock.o: $(INCLUDE)/lock.h lock.c 24 | $(CC) $(CFLAGS) -c lock.c 25 | 26 | log.o: $(INCLUDE)/log.h log.c 27 | $(CC) $(CFLAGS) -c log.c 28 | 29 | polling_policy.o: $(INCLUDE)/polling_policy.h polling_policy.c 30 | $(CC) $(CFLAGS) -c polling_policy.c 31 | 32 | reactor.o: $(INCLUDE)/reactor.h reactor.c 33 | $(CC) $(CFLAGS) -c reactor.c 34 | 35 | signal.o: $(INCLUDE)/signal.h signal.c 36 | $(CC) $(CFLAGS) -c signal.c 37 | 38 | timer.o: $(INCLUDE)/timer.h timer.c 39 | $(CC) $(CFLAGS) -c timer.c 40 | 41 | utility.o: $(INCLUDE)/utility.h utility.c 42 | $(CC) $(CFLAGS) -c utility.c 43 | 44 | clean: 45 | rm *.o 46 | rm $(PROGRAM) 47 | 48 | install: 49 | cp -r $(INCLUDE) /usr/include 50 | cp $(PROGRAM) /usr/lib 51 | -------------------------------------------------------------------------------- /src/cheetah/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_ 2 | #define CONFIG_H_ 3 | 4 | #define HAVE_SELECT 5 | 6 | #define HAVE_POLL 7 | 8 | #define HAVE_EPOLL 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/cheetah/event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #ifndef EVENT_H_ 24 | #define EVENT_H_ 25 | 26 | #include 27 | 28 | #include "list.h" 29 | #include "utility.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C"{ 33 | #endif 34 | 35 | #define E_READ 0x1 36 | #define E_WRITE 0x2 37 | #define E_SIGNAL 0x4 38 | #define E_TIMEOUT 0x8 39 | #define E_EDGE 0x10/* edge triggered */ 40 | #define E_ONCE 0x20/* one-time event */ 41 | #define E_IN_REACTOR 0x40/* indicates whether the event is in the reactor */ 42 | /* 43 | * The timer event's initial timerheap_idx value, 44 | * indicates that the event is not in the heap. 45 | */ 46 | #define E_OUT_OF_TIMERHEAP 0 47 | 48 | struct reactor; 49 | 50 | typedef void (*event_callback)(el_socket_t fd, short res_flags, void *arg); 51 | struct event{ 52 | struct list_head event_link; 53 | struct list_head pending_link; 54 | struct list_head hash_link; 55 | /* 56 | * For I/O events, fd stores the file descriptor being listened. 57 | * For signal events, fd stores the signal number being listened. 58 | * For timer events, fd stores the timer interval in millisecond. 59 | */ 60 | el_socket_t fd; 61 | 62 | /* Event type bitmask */ 63 | short ev_flags; 64 | /* Back pointer of the reactor holds the event */ 65 | struct reactor * rc; 66 | 67 | /* The events have been set after polling */ 68 | short res_flags; 69 | 70 | event_callback callback; 71 | void * callback_arg; 72 | 73 | /* Only used for timer event */ 74 | int timerheap_idx; 75 | }; 76 | 77 | /* 78 | * Create and initialize a event. 79 | * For I/O events, fd stores the file descriptor being listened. 80 | * For signal events, fd stores the signal number being listened. 81 | * For timer events, fd stores the timer interval in millisecond. 82 | * Return the newly created event. 83 | * @fd: the file descriptor to listen 84 | * @ev_flags: interested events bitmask 85 | * @call_back: the function to call when the specific events has occured 86 | * @arg: call_back data 87 | */ 88 | struct event * event_new(el_socket_t fd, short ev_flags,event_callback callback, void * arg); 89 | 90 | /* 91 | * Initialize a given event. 92 | * For I/O events, fd stores the file descriptor being listened. 93 | * For signal events, fd stores the signal number being listened. 94 | * For timer events, fd stores the timer interval in millisecond. 95 | * @e: event to initialize 96 | * @fd: the file descriptor to listen 97 | * @ev_flags: interested events bitmask 98 | * @call_back: the function to call when the specific events has occured 99 | * @arg: call_back data 100 | */ 101 | void event_set(struct event * e, el_socket_t fd, short ev_flags,event_callback callback, void * arg); 102 | 103 | /* 104 | * Modify the interested events of a event. 105 | * For I/O events, fd stores the file descriptor being listened. 106 | * For signal events, fd stores the signal number being listened. 107 | * For timer events, fd stores the timer interval in millisecond. 108 | * @e: event to modify 109 | * @ev_flags: new interested events bitmask 110 | */ 111 | void event_modify_events(struct event * e, short ev_flags); 112 | 113 | /* 114 | * Tests whether the event is in the reactor. 115 | * Return: 0 for false, 1 for true. 116 | * @e: event to test. 117 | */ 118 | int event_in_reactor(struct event * e); 119 | #ifdef __cplusplus 120 | } 121 | #endif 122 | #endif /*EVENT_H_*/ -------------------------------------------------------------------------------- /src/cheetah/event_ht.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* a hash table for event */ 24 | #ifndef EVENT_HT_H_ 25 | #define EVENT_HT_H_ 26 | 27 | #include "event.h" 28 | #include "log.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C"{ 32 | #endif 33 | 34 | struct event_ht{ 35 | struct list_head *table; 36 | /* The index of event_ht_primes we are using as the size of the hash table */ 37 | int p_index; 38 | /* We should expand the hash table if the threshold has been exceeded. */ 39 | int load_limit; 40 | /* The load factor we apply to the hash table */ 41 | double load_factor; 42 | /* the number of entries this hash table has */ 43 | int n_entries; 44 | /* The number of slots this hash table has */ 45 | int len; 46 | }; 47 | 48 | /* 49 | * Thomas Wang's hash function 50 | * @key: key to be hashed 51 | */ 52 | unsigned event_ht_hash(unsigned key); 53 | 54 | /* 55 | * Initialize the event hash table. 56 | * @ht: &struct event_ht to be initialized. 57 | * @load_factor: the load factor we apply on the hash table. 58 | */ 59 | int event_ht_init(struct event_ht * ht, double load_factor); 60 | 61 | /* 62 | * Insert a event into the hash table. 63 | * Do nothing if the event is already in the table, 64 | * @ht: &struct event_ht into which the event to be inserted 65 | * @event: &struct event entry to be inserted 66 | * @key: hash key 67 | */ 68 | int event_ht_insert(struct event_ht * ht, struct event * new_entry, unsigned key); 69 | 70 | /* 71 | * Insert a event into the hash table. 72 | * Replace old event by new one if the old event is already in the table. 73 | * @ht: &struct event_ht into which the event to be inserted 74 | * @event: &struct event entry to be inserted 75 | * @key: hash key 76 | */ 77 | int event_ht_insert_replace(struct event_ht * ht, struct event * new_entry, unsigned key); 78 | 79 | /* 80 | * Delete the event with the key from the hash table. 81 | * Do nothing if there is no matching key. 82 | * @ht: &struct event_ht from which the event will be deleted 83 | * @key: hash key 84 | */ 85 | void event_ht_delete_by_key(struct event_ht * ht, unsigned key); 86 | 87 | /* 88 | * Delete the event with the key from the hash table. 89 | * Do nothing if there is no matching key. 90 | * @ht: &struct event_ht from which the event will be deleted 91 | * @key: hash key 92 | */ 93 | int event_ht_delete(struct event_ht * ht, struct event * e); 94 | 95 | 96 | /* 97 | * Retrieve the coressponding event from the hash table. 98 | * Return null if there is no matching key. 99 | * @ht: &struct event_ht from which the event will be retrieved 100 | * @key: hash key 101 | */ 102 | struct event * event_ht_retrieve(struct event_ht * ht, unsigned key); 103 | 104 | /* 105 | * Free up the hash table. 106 | * @ht: the hash table 107 | */ 108 | void event_ht_free(struct event_ht * ht); 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | #endif /*EVENT_H_*/ -------------------------------------------------------------------------------- /src/cheetah/includes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #ifndef EL_INCLUDES_H_ 24 | #define EL_INCLUDES_H_ 25 | #ifdef WIN32 26 | #include 27 | #include 28 | #else 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #endif 37 | #endif 38 | -------------------------------------------------------------------------------- /src/cheetah/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #ifndef LIST_H_ 24 | #define LIST_H_ 25 | 26 | #ifdef __cplusplus 27 | extern "C"{ 28 | #endif 29 | 30 | struct list_head{ 31 | struct list_head *prev, *next; 32 | }; 33 | 34 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 35 | #define container_of(ptr, type, member) (((type*)((char*)ptr - (offsetof(type, member))))) 36 | 37 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 38 | 39 | #define LIST_HEAD(name) \ 40 | struct list_head name = LIST_HEAD_INIT(name) 41 | 42 | void INIT_LIST_HEAD(struct list_head *list); 43 | 44 | void INIT_LIST_EMPTY(struct list_head *list); 45 | 46 | /** 47 | * list_add_tail - add a new entry 48 | * @new_entry: new entry to be added 49 | * @head: list head to add it before 50 | * 51 | * Insert a new entry before the specified head. 52 | * This is useful for implementing queues. 53 | */ 54 | void list_add(struct list_head *new_entry, struct list_head *head); 55 | 56 | /** 57 | * list_add_tail - add a new entry 58 | * @new_entry: new entry to be added 59 | * @head: list head to add it before 60 | * 61 | * Insert a new entry before the specified head. 62 | * This is useful for implementing queues. 63 | */ 64 | void list_add_tail(struct list_head *new_entry, struct list_head *head); 65 | 66 | /** 67 | * list_del - deletes entry from list. 68 | * @entry: the element to delete from the list. 69 | * Note: list_empty() on entry does not return true after this, the entry is 70 | * in an undefined state. 71 | */ 72 | void list_del(struct list_head *entry); 73 | 74 | /** 75 | * list_empty - tests whether a list is empty 76 | * @head: the list to test. 77 | */ 78 | int list_empty(const struct list_head *head); 79 | /** 80 | * list_entry - get the struct for this entry 81 | * @ptr: the &struct list_head pointer. 82 | * @type: the type of the struct this is embedded in. 83 | * @member: the name of the list_struct within the struct. 84 | */ 85 | #define list_entry(ptr, type, member) \ 86 | container_of(ptr, type, member) 87 | 88 | /** 89 | * list_first_entry - get the first element from a list 90 | * @ptr: the list head to take the element from. 91 | * @type: the type of the struct this is embedded in. 92 | * @member: the name of the list_struct within the struct. 93 | * 94 | * Note, that list is expected to be not empty. 95 | */ 96 | #define list_first_entry(ptr, type, member) \ 97 | list_entry((ptr)->next, type, member) 98 | 99 | /** 100 | * list_last_entry - get the last element from a list 101 | * @ptr: the list head to take the element from. 102 | * @type: the type of the struct this is embedded in. 103 | * @member: the name of the list_struct within the struct. 104 | * 105 | * Note, that list is expected to be not empty. 106 | */ 107 | #define list_last_entry(ptr, type, member) \ 108 | list_entry((ptr)->prev, type, member) 109 | 110 | /** 111 | * list_first_entry_or_null - get the first element from a list 112 | * @ptr: the list head to take the element from. 113 | * @type: the type of the struct this is embedded in. 114 | * @member: the name of the list_struct within the struct. 115 | * 116 | * Note that if the list is empty, it returns NULL. 117 | */ 118 | #define list_first_entry_or_null(ptr, type, member) \ 119 | (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) 120 | 121 | /** 122 | * list_next_entry - get the next element in list 123 | * @pos: the type * to cursor 124 | * @member: the name of the list_struct within the struct. 125 | */ 126 | #define list_next_entry(pos, member) \ 127 | list_entry((pos)->member.next, typeof(*(pos)), member) 128 | 129 | /** 130 | * list_prev_entry - get the prev element in list 131 | * @pos: the type * to cursor 132 | * @member: the name of the list_struct within the struct. 133 | */ 134 | #define list_prev_entry(pos, member) \ 135 | list_entry((pos)->member.prev, typeof(*(pos)), member) 136 | 137 | /** 138 | * list_for_each - iterate over a list 139 | * @pos: the &struct list_head to use as a loop cursor. 140 | * @head: the head for your list. 141 | */ 142 | #define list_for_each(pos, head) \ 143 | for (pos = (head)->next; pos != (head); pos = pos->next) 144 | 145 | 146 | #ifdef __cplusplus 147 | } 148 | #endif 149 | #endif /*LIST_H_*/ -------------------------------------------------------------------------------- /src/cheetah/lock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* a simple lock implementation */ 24 | #ifndef EL_LOCK_H_ 25 | #define EL_LOCK_H_ 26 | #include "utility.h" 27 | #ifdef WIN32 28 | #include //for CriticalSection 29 | #else 30 | #include //for pthread_mutex 31 | #endif 32 | 33 | 34 | int el_lock_init(el_lock * lock); 35 | void el_lock_lock(el_lock * lock); 36 | void el_lock_unlock(el_lock * lock); 37 | void el_lock_destroy(el_lock * lock); 38 | 39 | #endif/* EL_LOCK_H_ */ -------------------------------------------------------------------------------- /src/cheetah/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* a simple logger implementation */ 24 | #ifndef LOG_H_ 25 | #define LOG_H_ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef WIN32 32 | #define __func__ __FUNCSIG__ 33 | #endif 34 | 35 | #ifndef NOLOG 36 | #define LOG_TO_STDERR 37 | #if (defined(LOG_TO_FILE) && defined(LOG_TO_STDERR)) 38 | #define LOG(...)\ 39 | log_file_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 40 | log_stderr_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 41 | 42 | #define LOG_EXIT(ret, ...)\ 43 | log_file_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 44 | log_stderr_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 45 | __print_exit(ret) 46 | #elif (!defined(LOG_TO_FILE) && defined(LOG_TO_STDERR)) 47 | #define LOG(...)\ 48 | log_stderr_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 49 | 50 | #define LOG_EXIT(ret, ...)\ 51 | log_stderr_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 52 | __print_exit(ret) 53 | #elif (defined(LOG_TO_FILE) && !defined(LOG_TO_STDERR)) 54 | #define LOG(...)\ 55 | log_file_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 56 | 57 | #define LOG_EXIT(ret, ...)\ 58 | log_file_print(__FILE__, __func__, __LINE__, __VA_ARGS__); \ 59 | __print_exit(ret) 60 | #else 61 | #define LOG(...) 62 | 63 | #define LOG_EXIT(ret, ...)\ 64 | exit(ret); 65 | #endif 66 | #else 67 | #define LOG(...) 68 | 69 | #define LOG_EXIT(ret, ...)\ 70 | exit(ret); 71 | #endif 72 | 73 | void log_file_print(const char * filename, const char * func, int line, const char *fmt, ...); 74 | void log_stderr_print(const char * filename, const char * func, int line, const char *fmt, ...); 75 | void __print_exit(int ret); 76 | #endif /* LOG_H_ */ 77 | -------------------------------------------------------------------------------- /src/cheetah/polling_policy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* generic polling policy */ 24 | #ifndef POLLING_POLICY_H_ 25 | #define POLLING_POLICY_H_ 26 | #include "utility.h" 27 | #include "reactor.h" 28 | #ifdef __cplusplus 29 | extern "C"{ 30 | #endif 31 | struct reactor; 32 | struct polling_policy{ 33 | /* the policy name */ 34 | const char * name; 35 | 36 | /* 37 | * Initialize the polling policy. 38 | * Return: NULL on failure, the policy internal data on success. 39 | * @r: the reactor uses this policy. 40 | */ 41 | void * (*init)(struct reactor * r); 42 | 43 | /* 44 | * Add a fd to this polling policy. 45 | * Return: -1 on failure, 0 on success 46 | * @r: the reactor uses this policy. 47 | * @fd: the fd to add. 48 | * @flags: the events interested. 49 | */ 50 | int (*add)(struct reactor * r, el_socket_t fd, short flags); 51 | 52 | /* 53 | * Modify the interested events of a fd. 54 | * Return: -1 on failure, 0 on success 55 | * @r: the reactor uses this policy. 56 | * @fd: the fd to add. 57 | * @flags: the events interested. 58 | */ 59 | int (*mod)(struct reactor * r, el_socket_t fd, short flags); 60 | 61 | /* 62 | * Delete a fd from this polling policy. 63 | * @r: the reactor uses this policy. 64 | * @fd: the fd to remove. 65 | * @flags: the events interested. 66 | */ 67 | int (*del)(struct reactor * r, el_socket_t fd, short flags); 68 | 69 | /* 70 | * Start polling. 71 | * Return: the number of fd on which some events have occured. 72 | * @r: the reactor that handles events. 73 | * @timeout: the time after which the poll will return in any case. 74 | */ 75 | int (*poll)(struct reactor * r, struct timeval * timeout); 76 | 77 | /* 78 | * frees up the related resources used by the polling policy 79 | * @r: the reactor that handles events. 80 | */ 81 | void (*destroy)(struct reactor * r); 82 | 83 | /* for debugging */ 84 | void (*print)(struct reactor * r); 85 | }; 86 | 87 | 88 | void * epoll_init(struct reactor * r); 89 | int epoll_add(struct reactor * r, el_socket_t fd, short flags); 90 | int epoll_del(struct reactor * r, el_socket_t fd, short flags); 91 | int epoll_poll(struct reactor * r, struct timeval * timeout); 92 | void epoll_destroy(struct reactor * r); 93 | void epoll_print(struct reactor * r); 94 | 95 | 96 | void * poll_init(struct reactor * r); 97 | int poll_add(struct reactor * r, el_socket_t fd, short flags); 98 | int poll_del(struct reactor * r, el_socket_t fd, short flags); 99 | int poll_poll(struct reactor * r, struct timeval * timeout); 100 | void poll_destroy(struct reactor * r); 101 | void poll_print(struct reactor * r); 102 | 103 | void * select_init(struct reactor * r); 104 | int select_add(struct reactor * r, el_socket_t fd, short flags); 105 | int select_del(struct reactor * r, el_socket_t fd, short flags); 106 | int select_poll(struct reactor * r, struct timeval * timeout); 107 | void select_destroy(struct reactor * r); 108 | void select_print(struct reactor * r); 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | #endif /* POLLING_POLICY_H_ */ -------------------------------------------------------------------------------- /src/cheetah/reactor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | 24 | /* reactor pattern implementation */ 25 | #ifndef REACTOR_H_ 26 | #define REACTOR_H_ 27 | 28 | #include "utility.h" 29 | #include "event_ht.h" 30 | #include "lock.h" 31 | #include "event.h" 32 | #include "signal.h" 33 | #include "timer.h" 34 | 35 | #ifdef __cplusplus 36 | extern "C"{ 37 | #endif 38 | 39 | /* exits after having processed at least one active event */ 40 | #define REACTOR_ONCE 0x01 41 | struct polling_policy; 42 | struct reactor{ 43 | /* events registered */ 44 | struct list_head event_list; 45 | /* active events waiting for being processed */ 46 | struct list_head pending_list; 47 | 48 | struct polling_policy * policy; 49 | /* policy specified data, usually the back pointer of the reactor. */ 50 | void * policy_data; 51 | 52 | /* event hash table */ 53 | struct event_ht eht; 54 | 55 | /* 56 | * Lock to avoid race conditions. 57 | * if null we assume this reactor is in single-threaded environment. 58 | */ 59 | el_lock * lock; 60 | 61 | /* Unnamed pipe used to wake up the polling thread. */ 62 | el_socket_t pipe[2]; 63 | struct event pe; 64 | 65 | /* Signal event internal data */ 66 | struct signal_internal * psi; 67 | /* Unnamed pipe used to tell the reactor that a signal has occurred. */ 68 | el_socket_t sig_pipe[2]; 69 | struct event * sig_pe; 70 | 71 | /* Timer event internal data */ 72 | struct timerheap_internal * pti; 73 | /* Unnamed pipe used to tell the reactor that a timer has expired. */ 74 | el_socket_t timer_pipe[2]; 75 | struct event * timer_pe; 76 | 77 | /* Indicates whether we should stop polling. */ 78 | int out; 79 | }; 80 | 81 | /* 82 | * Wake up the polling thread and tell the reactor to get out the loop. 83 | * @r: the reactor to wake up. 84 | */ 85 | void reactor_get_out(struct reactor * r); 86 | 87 | /* 88 | * Wrapper function for the initialization of reactor with normal functionality(I/O). 89 | * @r: the reactor to be initialized. 90 | * @policy_name: the name of polling policy. 91 | */ 92 | void reactor_init(struct reactor * r, const char * policy_name); 93 | 94 | /* 95 | * Wrapper function for the initialization of reactor with multithreading supported. 96 | * @r: the reactor to be initialized. 97 | * @policy_name: the name of polling policy. 98 | */ 99 | void reactor_init_with_mt(struct reactor * r, const char * policy_name); 100 | 101 | /* 102 | * Wrapper function for the initialization of reactor with signal events supported. 103 | * @r: the reactor to be initialized. 104 | * @policy_name: the name of polling policy. 105 | */ 106 | void reactor_init_with_signal(struct reactor * r, const char * policy_name); 107 | 108 | /* 109 | * Wrapper function for the initialization of reactor with timer events supported. 110 | * @r: the reactor to be initialized. 111 | * @policy_name: the name of polling policy. 112 | */ 113 | void reactor_init_with_timer(struct reactor * r, const char * policy_name); 114 | 115 | /* 116 | * Wrapper function for the initialization of reactor with signal and timer events supported. 117 | * @r: the reactor to be initialized. 118 | * @policy_name: the name of polling policy. 119 | */ 120 | void reactor_init_with_signal_timer(struct reactor * r, const char * policy_name); 121 | 122 | /* 123 | * Wrapper function for the initialization of reactor with multithreading and signal events supported. 124 | * @r: the reactor to be initialized. 125 | * @policy_name: the name of polling policy. 126 | */ 127 | void reactor_init_with_mt_signal(struct reactor * r, const char * policy_name); 128 | 129 | /* 130 | * Wrapper function for the initialization of reactor with multithreading and timer events supported. 131 | * @r: the reactor to be initialized. 132 | * @policy_name: the name of polling policy. 133 | */ 134 | void reactor_init_with_mt_timer(struct reactor * r, const char * policy_name); 135 | 136 | /* 137 | * Wrapper function for the initialization of reactor with multithreading, signal and timer events supported. 138 | * @r: the reactor to be initialized. 139 | * @policy_name: the name of polling policy. 140 | */ 141 | void reactor_init_with_mt_signal_timer(struct reactor * r, const char * policy_name); 142 | 143 | /* 144 | * Frees up resources related to the reactor. 145 | * @r: the reactor to destroy. 146 | */ 147 | void reactor_destroy(struct reactor * r); 148 | /* 149 | * Add a event to the reactor. 150 | * Return: 0 on success, -1 if the event is already in the reactor. 151 | * @r: the reactor. 152 | * @e: event to be added. 153 | */ 154 | int reactor_add_event(struct reactor * r, struct event * e); 155 | 156 | /* 157 | * modify the interested events of a event in the reactor. 158 | * Return: 0 on success, -1 if the event is not in the reactor. 159 | * @r: the reactor. 160 | * @e: event to be modified. 161 | */ 162 | int reactor_modify_events(struct reactor * r, struct event * e); 163 | 164 | /* 165 | * Add a active event to the pending list waiting for processing. 166 | * Return: 0 on success, -1 if the event is already in the pending list. 167 | * @r: the reactor. 168 | * @e: the event to add. 169 | * @res_flags: The events have been set after polling. 170 | */ 171 | int reactor_add_to_pending(struct reactor * r, struct event * e, short res_flags); 172 | 173 | /* 174 | * Remove a event from the reactor. 175 | * Return: 0 on success, -1 if the event is not in the reactor. 176 | * @r: the reactor. 177 | * @e: event to be removed. 178 | */ 179 | int reactor_remove_event(struct reactor * r, struct event * e); 180 | 181 | /* 182 | * Test whether the given reactor has no event. 183 | * @r: the reactor to test. 184 | */ 185 | int reactor_event_empty(struct reactor * r); 186 | 187 | 188 | /* 189 | * Remove all events from the reactor. 190 | * @r: the reactor to clean 191 | */ 192 | void reactor_clean_events(struct reactor * r); 193 | /* 194 | * Start the reactor. 195 | * @r: the reactor to start. 196 | * @timeout: The time after which pollling policy will return. 197 | */ 198 | void reactor_loop(struct reactor * r, struct timeval * timeout, int flags); 199 | 200 | #ifdef __cplusplus 201 | } 202 | #endif 203 | #endif /*REACTOR_H_*/ -------------------------------------------------------------------------------- /src/cheetah/signal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #ifndef EL_SIGNAL_H_ 24 | #define EL_SIGNAL_H_ 25 | 26 | #include 27 | 28 | #define SIGNALN 65 29 | 30 | typedef void (* signal_handler)(int); 31 | 32 | struct event; 33 | struct reactor; 34 | struct signal_internal{ 35 | /* Used to restore original signal handler */ 36 | struct sigaction old_actions[SIGNALN]; 37 | 38 | /* 39 | * Every signal only has one registered event. 40 | * The last one will take effect if there 41 | * are multiple events registering to the 42 | * same signal. 43 | */ 44 | struct event * sigevents[SIGNALN]; 45 | }; 46 | 47 | /* 48 | * Signal handler to inform the reactor that a signal has occured. 49 | * @sig: the signal that just occured. 50 | */ 51 | void sig_handler(int sig); 52 | 53 | /* 54 | * Allocate and initialize the signal_internal structure. 55 | * Return: newly created signal_internal on success, NULL on failure. 56 | * @r: the reactor that handles signals. 57 | */ 58 | struct signal_internal * signal_internal_init(struct reactor * r); 59 | 60 | /* 61 | * Register a signal to the signal_internal. 62 | * Return: 0 on success, -1 on failure. 63 | * @r: the related reacotr. 64 | * @sig: the signal to regitser. 65 | * @e: the signal event. 66 | */ 67 | int signal_internal_register(struct reactor * r, int sig, struct event * e); 68 | 69 | /* 70 | * Unregister a signal from the signal_internal. 71 | * Return: 0 on success, -1 on failure. 72 | * @r: the related reacotr. 73 | * @sig: the signal to unregitser. 74 | */ 75 | int signal_internal_unregister(struct reactor * r, int sig); 76 | 77 | int signal_internal_restore_all(struct reactor * r); 78 | 79 | #endif -------------------------------------------------------------------------------- /src/cheetah/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #ifndef TIMER_H_ 24 | #define TIMER_H_ 25 | #include 26 | #include "utility.h" 27 | 28 | struct event; 29 | struct reactor; 30 | struct heap_entry{ 31 | /* The expiration of the event */ 32 | struct timeval expiration; 33 | struct event * e; 34 | }; 35 | 36 | struct timerheap_internal{ 37 | /* The number of entries in the heap */ 38 | int size; 39 | /* The capacity of the heap */ 40 | int capacity; 41 | /* The heap_entry array */ 42 | struct heap_entry * heap; 43 | }; 44 | 45 | /* 46 | * Add v milliseconds to the timeval t 47 | */ 48 | #define timer_add(t, v)do{ \ 49 | (t).tv_sec += (v) / 1000; \ 50 | (t).tv_usec += ((v) % 1000) * 1000; \ 51 | if((t).tv_usec > 1000000){ \ 52 | (t).tv_usec -= 1000000; \ 53 | (t).tv_sec++; \ 54 | } \ 55 | }while(0); 56 | 57 | /* 58 | * Substract v milliseconds from the timeval t 59 | */ 60 | #define timer_sub(t, v)do{ \ 61 | (t).tv_sec -= (v) / 1000; \ 62 | (t).tv_usec -= ((v) % 1000) * 1000; \ 63 | if((t).tv_usec < 0){ \ 64 | (t).tv_usec += 1000000; \ 65 | (t).tv_sec--; \ 66 | } \ 67 | }while(0); 68 | 69 | /* 70 | * Convert the time in millisecond to timerval. 71 | */ 72 | #define timer_to_tv(tv, tm)do{ \ 73 | (tv).tv_sec = (tm) / 1000; \ 74 | (tv).tv_usec = ((tm) % 1000) * 1000; \ 75 | }while(0); 76 | /* 77 | * Convert the timerval to time in millisecond. 78 | */ 79 | #define timer_to_ms(t) ((t).tv_sec * 1000 + (t).tv_usec / 1000) 80 | 81 | /* 82 | * Tests whether @t1 is greater or equal to @t2. 83 | */ 84 | #define timer_ge(t1, t2) ((t1).tv_sec > (t2).tv_sec || ((t1).tv_sec == (t2).tv_sec && (t1).tv_usec >= (t2).tv_usec)) 85 | 86 | /* 87 | * Tests whether @t1 is smaller or equal to @t2. 88 | */ 89 | #define timer_se(t1, t2) ((t1).tv_sec < (t2).tv_sec || ((t1).tv_sec == (t2).tv_sec && (t1).tv_usec <= (t2).tv_usec)) 90 | 91 | /* 92 | * Tests whether @t1 is greater to @t2. 93 | */ 94 | #define timer_g(t1, t2) ((t1).tv_sec > (t2).tv_sec || ((t1).tv_sec == (t2).tv_sec && (t1).tv_usec > (t2).tv_usec)) 95 | 96 | /* 97 | * Tests whether @t1 is smaller to @t2. 98 | */ 99 | #define timer_s(t1, t2) ((t1).tv_sec < (t2).tv_sec || ((t1).tv_sec == (t2).tv_sec && (t1).tv_usec < (t2).tv_usec)) 100 | 101 | 102 | #define TIMERHEAP_INIT_SIZE 32 103 | 104 | /* 105 | * Create and initialize a timerheap. 106 | * Return: a newly created timerheap_internal structure on success, NULL on failure. 107 | */ 108 | struct timerheap_internal * timerheap_internal_init(); 109 | 110 | /* 111 | * Tests whether the top entry is expired. 112 | * Return: 0 on false, 1 on true. 113 | * @r: the reactor which handles the timer events. 114 | */ 115 | int timerheap_top_expired(struct reactor * r); 116 | /* 117 | * Get the top entry's timeout value. 118 | * Return: the expiration stored in timeval on success, NULL on failure(The timerheap is empty). 119 | * @r: the reactor which handles the timer events. 120 | * @timeout: the timeval struct to hold the return val 121 | */ 122 | struct timeval * timerheap_top_timeout(struct reactor * r, struct timeval * timeout); 123 | /* 124 | * Retrieve the top event of the timerheap. 125 | * Return: the top entry of the heap on success, NULL if the heap is empty. 126 | * @r: the reactor which handles the timer events. 127 | */ 128 | struct event * timerheap_get_top(struct reactor * r); 129 | 130 | /* 131 | * Pop up the top event of the timerheap and reheapify the timerheap. 132 | * Return: the top entry of the heap on success, NULL if the heap is empty. 133 | * @r: the reactor which handles the timer events. 134 | */ 135 | struct event * timerheap_pop_top(struct reactor * r); 136 | 137 | /* 138 | * Add the timer event by its interval and reajust the heap. 139 | * @r: the reactor which handles the timer events. 140 | * @e: the timer event being manipulated. 141 | */ 142 | void timerheap_reset_timer(struct reactor * r, struct event * e); 143 | 144 | /* 145 | * Add timer event to the timerheap. 146 | * Return: 0 on success, -1 on failure. 147 | * @r: the reactor which handles the timer events. 148 | * @e: the timer event to add. 149 | */ 150 | int timerheap_add_event(struct reactor * r, struct event * e); 151 | 152 | /* 153 | * Remove timer event from the timerheap. 154 | * Return: 0 on success, -1 on failure. 155 | * @r: the reactor which handles the timer events. 156 | * @e: the timer event to remove. 157 | */ 158 | int timerheap_remove_event(struct reactor * r, struct event * e); 159 | 160 | /* 161 | * remove all timer events from the timerheap. 162 | * Return: 0 on success, -1 on failure. 163 | * @r: the reactor which handles the timer events. 164 | */ 165 | int timerheap_clean_events(struct reactor *r); 166 | 167 | /* 168 | * Free up the resources used by the timerheap and the timerheap_internal structure. 169 | * Return: 0 on success, -1 on failure. 170 | * @r: the reactor which handles the timer events. 171 | */ 172 | int timerheap_destroy(struct reactor * r); 173 | 174 | #endif -------------------------------------------------------------------------------- /src/cheetah/utility.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* utility stuff */ 24 | #ifndef EL_UTILITY_H_ 25 | #define EL_UTILITY_H_ 26 | #include "log.h" 27 | #include "includes.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C"{ 31 | #endif 32 | 33 | #ifdef WIN32 34 | #define el_socket_t intptr_t 35 | #define el_lock CRITICAL_SECTION 36 | #define el_timesub(lv, rv, ov)do{\ 37 | (ov)->tv_sec = (lv)->tv_sec - (rv)->tv_sec;\ 38 | (ov)->tv_usec = (lv)->tv_usec - (rv)->tv_usec;\ 39 | if((ov)->tv_usec < 0){\ 40 | (ov)->tv_usec += 1000000;\ 41 | (ov)->tv_sec--;\ 42 | }\ 43 | }while(0); 44 | #else 45 | #define el_socket_t int 46 | #define el_lock pthread_mutex_t 47 | #define el_timesub(lv, rv, ov)do{\ 48 | (ov)->tv_sec = (lv)->tv_sec - (rv)->tv_sec;\ 49 | (ov)->tv_usec = (lv)->tv_usec - (rv)->tv_usec;\ 50 | if((ov)->tv_usec < 0){\ 51 | (ov)->tv_usec += 1000000;\ 52 | (ov)->tv_sec--;\ 53 | }\ 54 | }while(0); 55 | #endif 56 | 57 | /* 58 | * Create a pair of connected sockets 59 | * Return: 0 on success, -1 on failure 60 | * @pipe: used to return the newly created sockets 61 | */ 62 | int el_create_pipe(el_socket_t pipe[2]); 63 | 64 | /* 65 | * Close a socket. 66 | * Return: 0 on success, -1 on failure 67 | * @fd: the socket file descriptor to close 68 | */ 69 | int el_close_fd(el_socket_t fd); 70 | 71 | /* 72 | * Get the system time and store it in the tv. 73 | * Return: -1 on failure, 0 on success 74 | * @tv: the timeval structure in which the time where be store 75 | */ 76 | int el_gettimeofday(struct timeval * tv); 77 | 78 | /* 79 | * Set the fd to nonblocking mode. 80 | * Return: -1 on failure, 0 on success. 81 | * @fd: the fd to set. 82 | */ 83 | int el_set_nonblocking(el_socket_t fd); 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | #endif /* EL_UTILITY_H_ */ 89 | -------------------------------------------------------------------------------- /src/configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CARROT_CONFIG="cheetah/config.h" 4 | 5 | rm -f $CARROT_CONFIG 6 | 7 | echo "#ifndef CONFIG_H_" > $CARROT_CONFIG 8 | echo -e "#define CONFIG_H_\n" >> $CARROT_CONFIG 9 | 10 | CARROT_TEST_FILE="test.c" 11 | CARROT_TEST_CFLAGS="-Wall -Werror -O0" 12 | CARROT_TEST_CC="gcc" 13 | 14 | echo -e "Checking for select... \c" 15 | cat << END > $CARROT_TEST_FILE 16 | #ifdef WIN32 17 | #include 18 | #else 19 | #include 20 | #include 21 | #include 22 | #endif 23 | 24 | int main(){ 25 | select(0, NULL, NULL, NULL, NULL); 26 | return 0; 27 | } 28 | END 29 | 30 | CARROT_TEST="$CARROT_TEST_CC $CARROT_TEST_CFLAGS \ 31 | -c $CARROT_TEST_FILE > /dev/null 2>&1" 32 | 33 | eval "$CARROT_TEST" 34 | 35 | if [ $? -eq 0 ]; then 36 | echo "yes" 37 | echo -e "#define HAVE_SELECT\n" >> $CARROT_CONFIG 38 | else 39 | echo "no" 40 | fi 41 | 42 | echo -e "Checking for poll... \c" 43 | cat << END > $CARROT_TEST_FILE 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | int main(){ 50 | poll(NULL, 0, 0); 51 | return 0; 52 | } 53 | END 54 | 55 | eval "$CARROT_TEST" 56 | 57 | if [ $? -eq 0 ]; then 58 | echo "yes" 59 | echo -e "#define HAVE_POLL\n" >> $CARROT_CONFIG 60 | else 61 | echo "no" 62 | fi 63 | 64 | echo -e "Checking for epoll... \c" 65 | cat << END > $CARROT_TEST_FILE 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | int main(){ 72 | epoll_wait(0, NULL, 0, -1); 73 | return 0; 74 | } 75 | END 76 | 77 | eval "$CARROT_TEST" 78 | 79 | if [ $? -eq 0 ]; then 80 | echo "yes" 81 | echo -e "#define HAVE_EPOLL\n" >> $CARROT_CONFIG 82 | else 83 | echo "no" 84 | fi 85 | 86 | echo -e "Checking for kqueue... \c" 87 | cat << END > $CARROT_TEST_FILE 88 | #include 89 | #include 90 | #include 91 | 92 | int main(){ 93 | (void)kqueue(); 94 | return 0; 95 | } 96 | END 97 | 98 | eval "$CARROT_TEST" 99 | 100 | if [ $? -eq 0 ]; then 101 | echo "yes" 102 | echo -e "#define HAVE_KQUEUE\n" >> $CARROT_CONFIG 103 | else 104 | echo "no" 105 | fi 106 | 107 | echo -e "Checking for /dev/poll... \c" 108 | cat << END > $CARROT_TEST_FILE 109 | #include 110 | #include 111 | #include 112 | 113 | int main(){ 114 | (void)kqueue(); 115 | return 0; 116 | } 117 | END 118 | 119 | eval "$CARROT_TEST" 120 | 121 | if [ $? -eq 0 ]; then 122 | echo "yes" 123 | echo -e "#define HAVE_DEVPOLL\n" >> $CARROT_CONFIG 124 | else 125 | echo "no" 126 | fi 127 | 128 | rm -rf $CARROT_TEST_FILE 129 | 130 | echo "#endif" >> $CARROT_CONFIG 131 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | 27 | #include "cheetah/event.h" 28 | 29 | /* 30 | * Create and initialize a new event. 31 | * Return: newly created event. 32 | * @fd: the file descriptor related. 33 | * @ev_flags: the events interested 34 | * @callback: the function to call when specific event has occrued 35 | * @arg: the arg which pass to the @callback, if null we will pass the event to the callback. 36 | */ 37 | inline struct event * event_new(el_socket_t fd, short ev_flags,event_callback callback, void * arg){ 38 | if(callback == NULL)return NULL; 39 | struct event * new_event = malloc(sizeof(struct event)); 40 | 41 | if(new_event == NULL)return NULL; 42 | new_event->ev_flags = ev_flags; 43 | new_event->callback = callback; 44 | new_event->callback_arg = arg ? arg : new_event; 45 | new_event->rc = NULL; 46 | new_event->fd = fd; 47 | new_event->timerheap_idx = E_OUT_OF_TIMERHEAP; 48 | INIT_LIST_EMPTY(&new_event->event_link); 49 | INIT_LIST_EMPTY(&new_event->pending_link); 50 | INIT_LIST_EMPTY(&new_event->hash_link); 51 | return new_event; 52 | } 53 | 54 | inline void event_set(struct event * e, el_socket_t fd, short ev_flags, event_callback callback, void * arg){ 55 | assert(e != NULL); 56 | 57 | e->ev_flags = ev_flags; 58 | e->callback = callback; 59 | e->callback_arg = arg; 60 | e->fd = fd; 61 | e->timerheap_idx = E_OUT_OF_TIMERHEAP; 62 | INIT_LIST_EMPTY(&e->event_link); 63 | INIT_LIST_EMPTY(&e->pending_link); 64 | INIT_LIST_EMPTY(&e->hash_link); 65 | } 66 | 67 | void event_modify_events(struct event * e, short ev_flags) { 68 | assert(e != NULL); 69 | 70 | e->ev_flags = ev_flags; 71 | } 72 | 73 | inline int event_in_reactor(struct event * e){ 74 | return e->ev_flags & E_IN_REACTOR; 75 | } -------------------------------------------------------------------------------- /src/event_ht.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | 26 | #include "cheetah/event_ht.h" 27 | #include "cheetah/list.h" 28 | static const int event_ht_primes[] = { 29 | 53, 97, 193, 389, 769, 1543, 3079, 6151, 30 | 12289, 24593, 49157, 98317, 196613, 393241, 31 | 786433, 1572869, 3145739, 3145739, 12582917, 32 | 25165843, 50331653, 100663319, 201326611, 33 | 402653189, 805306457, 1610612741 34 | }; 35 | static const int event_ht_nprimes = sizeof(event_ht_primes) / sizeof(int); 36 | 37 | inline unsigned event_ht_hash(unsigned key){ 38 | key = (~key) + (key << 21); // key = (key << 21) - key - 1; 39 | key = key ^ (key >> 24); 40 | key = (key + (key << 3)) + (key << 8); // key * 265 41 | key = key ^ (key >> 14); 42 | key = (key + (key << 2)) + (key << 4); // key * 21 43 | key = key ^ (key >> 28); 44 | key = key + (key << 31); 45 | return key; 46 | } 47 | 48 | /* 49 | * Expand the size of the hash table to @size. 50 | * @ht: the hash table to expand 51 | * @size: the size we expand to 52 | */ 53 | static int event_ht_expand(struct event_ht * ht, int size){ 54 | int new_len, new_idx, new_load_limit, i; 55 | struct list_head * new_table, *p, *q, *head; 56 | struct event * entry; 57 | unsigned h; 58 | new_load_limit = ht->load_limit; 59 | new_len = ht->len; 60 | new_idx = ht->p_index; 61 | while(new_load_limit < size && new_idx < event_ht_nprimes){ 62 | new_len = event_ht_primes[++new_idx]; 63 | new_load_limit = ht->load_factor * new_len; 64 | } 65 | 66 | if((new_table = malloc(new_len * sizeof(struct list_head))) == NULL){ 67 | LOG("failed to malloc: %s", strerror(errno)); 68 | return (-1); 69 | } 70 | 71 | for(i = 0; i < new_len; ++i){ 72 | INIT_LIST_HEAD(&new_table[i]); 73 | } 74 | 75 | /* 76 | * Rehash and move all event to new_table. 77 | */ 78 | for(i = 0; i < ht->len; ++i){ 79 | head = &(ht->table[i]); 80 | if(!list_empty(head)){ 81 | p = head->next; 82 | while(p != head){ 83 | q = p->next; 84 | entry = list_entry(p, struct event, hash_link); 85 | list_del(p); 86 | h = event_ht_hash(entry->fd) % new_len; 87 | list_add_tail(&entry->hash_link, &new_table[h]); 88 | p = q; 89 | } 90 | } 91 | } 92 | 93 | free(ht->table); 94 | 95 | ht->p_index = new_idx; 96 | ht->table = new_table; 97 | ht->len = new_len; 98 | ht->load_limit = new_load_limit; 99 | 100 | return (0); 101 | } 102 | 103 | inline int event_ht_init(struct event_ht * ht, double load_factor){ 104 | int i, idx; 105 | 106 | idx = 0; 107 | ht->p_index = 0; 108 | ht->load_limit = load_factor * event_ht_primes[idx]; 109 | ht->load_factor = load_factor; 110 | ht->n_entries = 0; 111 | ht->len = event_ht_primes[idx]; 112 | 113 | ht->table = malloc(ht->len * sizeof(struct list_head)); 114 | if(ht->table == NULL){ 115 | LOG("memory shortage."); 116 | return (-1); 117 | } 118 | for(i = 0; i < ht->len; ++i){ 119 | INIT_LIST_HEAD(&ht->table[i]); 120 | } 121 | return (0); 122 | } 123 | 124 | inline int event_ht_insert(struct event_ht * ht, struct event * new, unsigned key){ 125 | unsigned h; 126 | 127 | if(new->hash_link.prev || new->hash_link.next){ 128 | /* 129 | * This event is already in the hash table. 130 | * Assume every event only can be in one reactor. 131 | */ 132 | return (-1); 133 | } 134 | 135 | /* expand the hash table if nessesary */ 136 | if(ht->n_entries >= ht->load_limit) 137 | event_ht_expand(ht, ht->n_entries + 1); 138 | 139 | h = event_ht_hash(key) % ht->len; 140 | list_add_tail(&new->hash_link, &ht->table[h]); 141 | ++(ht->n_entries); 142 | return (0); 143 | } 144 | 145 | inline int event_ht_insert_replace(struct event_ht * ht, struct event * new, unsigned key){ 146 | unsigned h; 147 | 148 | if(new->hash_link.prev || new->hash_link.next){ 149 | /* 150 | * This event is not in the hash table. 151 | * Assume every event only can be in one reactor. 152 | */ 153 | return (0); 154 | } 155 | 156 | /* expand the hash table if nessesary */ 157 | if(ht->n_entries >= ht->load_limit) 158 | event_ht_expand(ht, ht->n_entries + 1); 159 | 160 | /* rehash the key */ 161 | h = event_ht_hash(key) % ht->len; 162 | list_add_tail(&new->hash_link, &ht->table[h]); 163 | ++(ht->n_entries); 164 | return (0); 165 | } 166 | 167 | inline void event_ht_delete_by_key(struct event_ht * ht, unsigned key){ 168 | struct list_head *p; 169 | unsigned h; 170 | h = event_ht_hash(key) % ht->len; 171 | 172 | 173 | list_for_each(p, &ht->table[h]){ 174 | struct event * entry = list_entry(p, struct event, hash_link); 175 | if(entry->fd == key){ 176 | list_del(p); 177 | --(ht->n_entries); 178 | return; 179 | } 180 | } 181 | } 182 | 183 | inline int event_ht_delete(struct event_ht * ht, struct event * e){ 184 | 185 | if(e->hash_link.prev == NULL || e->hash_link.next == NULL){ 186 | /* 187 | * This event is not in the hash table. 188 | * Assume every event only can be in one reactor. 189 | */ 190 | return (-1); 191 | } 192 | 193 | list_del(&e->hash_link); 194 | --(ht->n_entries); 195 | return (0); 196 | } 197 | 198 | inline struct event * event_ht_retrieve(struct event_ht * ht, unsigned key){ 199 | unsigned h; 200 | struct list_head *p; 201 | 202 | h = event_ht_hash(key) % ht->len; 203 | 204 | list_for_each(p, &ht->table[h]){ 205 | struct event * entry = list_entry(p, struct event, hash_link); 206 | if(entry->fd == key){ 207 | return entry; 208 | } 209 | } 210 | 211 | return NULL; 212 | } 213 | inline void event_ht_free(struct event_ht * ht){ 214 | free(ht->table); 215 | } 216 | /* 217 | * Print out the whole slot of events with given key for debugging 218 | * @ht: &struct event_ht to iterate 219 | * @key: hash key 220 | */ 221 | inline struct event * event_ht_iterate(struct event_ht * ht, unsigned key){ 222 | unsigned h; 223 | struct list_head *p; 224 | 225 | h = event_ht_hash(key) % ht->len; 226 | list_for_each(p, &ht->table[h]){ 227 | struct event * entry = list_entry(p, struct event, hash_link); 228 | printf("key:%d, fd:%d\n", key, entry->fd); 229 | } 230 | return NULL; 231 | } -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include "cheetah/list.h" 24 | void INIT_LIST_HEAD(struct list_head *list) 25 | { 26 | list->next = list; 27 | list->prev = list; 28 | } 29 | 30 | void INIT_LIST_EMPTY(struct list_head *list){ 31 | list->next = list->prev = 0; 32 | } 33 | /* 34 | * Insert a new entry between two known consecutive entries. 35 | * 36 | * This is only for internal list manipulation where we know 37 | * the prev/next entries already! 38 | */ 39 | static void __list_add(struct list_head *new, 40 | struct list_head *prev, 41 | struct list_head *next) 42 | { 43 | next->prev = new; 44 | new->next = next; 45 | new->prev = prev; 46 | prev->next = new; 47 | } 48 | 49 | /** 50 | * list_add_tail - add a new entry 51 | * @new: new entry to be added 52 | * @head: list head to add it before 53 | * 54 | * Insert a new entry before the specified head. 55 | * This is useful for implementing queues. 56 | */ 57 | void list_add(struct list_head *new, struct list_head *head) 58 | { 59 | __list_add(new, head, head->next); 60 | } 61 | 62 | /** 63 | * list_add_tail - add a new entry 64 | * @new: new entry to be added 65 | * @head: list head to add it before 66 | * 67 | * Insert a new entry before the specified head. 68 | * This is useful for implementing queues. 69 | */ 70 | void list_add_tail(struct list_head *new, struct list_head *head) 71 | { 72 | __list_add(new, head->prev, head); 73 | } 74 | 75 | 76 | /* 77 | * Delete a list entry by making the prev/next entries 78 | * point to each other. 79 | * 80 | * This is only for internal list manipulation where we know 81 | * the prev/next entries already! 82 | */ 83 | static void __list_del(struct list_head * prev, struct list_head * next) 84 | { 85 | next->prev = prev; 86 | prev->next = next; 87 | } 88 | 89 | /** 90 | * list_del - deletes entry from list and set link to be null. 91 | * @entry: the element to delete from the list. 92 | * Note: list_empty() on entry does not return true after this, the entry is 93 | * in an undefined state. 94 | */ 95 | void list_del(struct list_head *entry) 96 | { 97 | if(entry == 0 || entry->prev == 0 || entry->next == 0)return; 98 | 99 | __list_del(entry->prev, entry->next); 100 | entry->prev = entry->next = 0; 101 | } 102 | 103 | /** 104 | * list_empty - tests whether a list is empty 105 | * @head: the list to test. 106 | */ 107 | int list_empty(const struct list_head *head) 108 | { 109 | return head->next == head; 110 | } -------------------------------------------------------------------------------- /src/lock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include "cheetah/lock.h" 24 | #include "cheetah/log.h" 25 | 26 | int el_lock_init(el_lock * lock){ 27 | if(!lock)return 0; 28 | #ifdef WIN32 29 | return InitializeCriticalSection(lock); 30 | #else 31 | return pthread_mutex_init(lock, NULL); 32 | #endif 33 | } 34 | 35 | void el_lock_lock(el_lock * lock){ 36 | if(!lock)return; 37 | #ifdef WIN32 38 | EnterCriticalSection(lock); 39 | #else 40 | pthread_mutex_lock(lock); 41 | #endif 42 | 43 | } 44 | 45 | void el_lock_unlock(el_lock * lock){ 46 | if(!lock)return; 47 | #ifdef WIN32 48 | LeaveCriticalSection(lock); 49 | #else 50 | pthread_mutex_unlock(lock); 51 | #endif 52 | } 53 | 54 | void el_lock_destroy(el_lock * lock){ 55 | if(!lock)return; 56 | #ifdef WIN32 57 | DeleeCriticalSection(lock); 58 | #else 59 | pthread_mutex_destroy(lock); 60 | #endif 61 | } -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cheetah/log.h" 30 | 31 | static FILE * fp; 32 | static int counter; 33 | static char log_filename[64]; 34 | 35 | void log_file_print(const char * filename, const char * func, int line, const char *fmt, ...){ 36 | time_t t; 37 | struct tm *now; 38 | va_list list; 39 | 40 | time(&t); 41 | now = localtime(&t); 42 | 43 | if(++counter == 1){ 44 | sprintf(log_filename, "%ld.log", t); 45 | fp = fopen(log_filename, "w"); 46 | } 47 | else{ 48 | fp = fopen(log_filename, "a+"); 49 | } 50 | 51 | if(fp == NULL){ 52 | perror("__log_file_print"); 53 | exit(0); 54 | } 55 | 56 | pid_t pid = getpid(); 57 | fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d pid %d", now->tm_year + 1900, 58 | now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, 59 | now->tm_sec, pid); 60 | fprintf(fp, "[%s]:[%s]:[line: %d]: ", filename, func, line); 61 | 62 | 63 | va_start(list, fmt); 64 | vfprintf(fp, fmt, list); 65 | va_end(list); 66 | 67 | fputc('\n', fp); 68 | fclose(fp); 69 | } 70 | 71 | void log_stderr_print(const char * filename, const char * func, int line, const char *fmt, ...){ 72 | time_t t; 73 | struct tm *now; 74 | va_list list; 75 | 76 | time(&t); 77 | now = localtime(&t); 78 | 79 | pid_t pid = getpid(); 80 | fprintf(stderr, "%04d-%02d-%02d %02d:%02d:%02d pid %d", now->tm_year + 1900, 81 | now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, 82 | now->tm_sec, pid); 83 | fprintf(stderr, "[%s]:[%s]:[line %d]: ", filename, func, line); 84 | 85 | 86 | va_start(list, fmt); 87 | vfprintf(stderr, fmt, list); 88 | va_end(list); 89 | 90 | fputc('\n', stderr); 91 | } 92 | 93 | void __print_exit(int ret){ 94 | exit(ret); 95 | } 96 | -------------------------------------------------------------------------------- /src/log_test.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | int main(int argc, char const *argv[]){ 4 | LOG_EXIT(-1, "sb250 %d", 1); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /src/polling_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* epoll polling policy */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cheetah/polling_policy.h" 30 | #include "cheetah/includes.h" 31 | #include "cheetah/log.h" 32 | 33 | #define EPOLL_INIT_EVENT_SIZE 32 34 | 35 | struct epoll_internal{ 36 | int epoll_fd; 37 | int n_events; 38 | int max_events; 39 | struct epoll_event * events; 40 | }; 41 | 42 | /* 43 | * Resize the events to given size. 44 | * Return: -1 on failure, 0 on success. 45 | * @pei: the internal data used by epoll polling policy. 46 | * @size: the size that we should resize to. 47 | */ 48 | static int epoll_resize(struct epoll_internal * pei, int size){ 49 | struct epoll_event * pee; 50 | assert(pei != NULL); 51 | if(pei == NULL){ 52 | LOG("pei is null!!"); 53 | return (-1); 54 | } 55 | 56 | if((pee = realloc(pei->events, size * sizeof(struct epoll_event))) == NULL){ 57 | LOG("failed to realloc for events, maybe run out of memory."); 58 | return (-1); 59 | } 60 | 61 | pei->events = pee; 62 | pei->max_events = size; 63 | return (0); 64 | } 65 | 66 | /* 67 | * Create and initialize the internal data used by epoll polling policy. 68 | * Return value: newly created internal data on success, NULL on failure. 69 | * @r: the reactor which uses this policy. 70 | */ 71 | void * epoll_init(struct reactor * r){ 72 | struct epoll_internal * ret; 73 | 74 | assert(r); 75 | if(r == NULL){ 76 | LOG("r is null!!"); 77 | return NULL; 78 | } 79 | 80 | if((ret = malloc(sizeof(struct epoll_internal))) == NULL){ 81 | LOG("failed to malloc for epoll_internal"); 82 | return NULL; 83 | } 84 | 85 | memset(ret, 0, sizeof(struct epoll_internal)); 86 | 87 | if((ret->epoll_fd = epoll_create(EPOLL_INIT_EVENT_SIZE)) == -1){ 88 | LOG("failed on epoll_create"); 89 | free(ret); 90 | return NULL; 91 | } 92 | 93 | if(epoll_resize(ret, EPOLL_INIT_EVENT_SIZE) == -1){ 94 | LOG("failed on epoll_resize"); 95 | close(ret->epoll_fd); 96 | free(ret); 97 | return NULL; 98 | } 99 | 100 | return ret; 101 | } 102 | 103 | /* 104 | * Frees up the internal data used by epoll polling policy. 105 | * @pei: the internal data. 106 | */ 107 | static void epoll_free(struct epoll_internal * pei){ 108 | assert(pei != NULL); 109 | if(pei == NULL){ 110 | LOG("pei is null!!"); 111 | return; 112 | } 113 | 114 | if(pei->events){ 115 | free(pei->events); 116 | pei->events = NULL; 117 | } 118 | if(pei->epoll_fd >= 0){ 119 | close(pei->epoll_fd); 120 | } 121 | 122 | free(pei); 123 | } 124 | 125 | /* 126 | * Clean up the policy internal data 127 | * @r: the reactor which uses this policy 128 | */ 129 | void epoll_destroy(struct reactor * r){ 130 | assert(r != NULL); 131 | if(r == NULL){ 132 | LOG("r is null!!"); 133 | return; 134 | } 135 | epoll_free(r->policy_data); 136 | } 137 | 138 | static inline int epoll_setup_mask(short flags){ 139 | int ret = 0; 140 | if(flags & E_READ){ 141 | ret |= EPOLLIN | EPOLLPRI; 142 | } 143 | if(flags & E_WRITE){ 144 | ret |= EPOLLOUT; 145 | } 146 | if(flags & E_EDGE){ 147 | ret |= EPOLLET; 148 | } 149 | if(flags & E_ONCE){ 150 | ret |= EPOLLONESHOT; 151 | } 152 | return ret; 153 | } 154 | 155 | static void epoll_print_error(struct epoll_internal * pei, el_socket_t fd){ 156 | if(errno == EBADF){ 157 | LOG("[epoll_fd %d]or [fd %d]is not valid!!", pei->epoll_fd, fd); 158 | }else if(errno == ENOENT){ 159 | LOG("[fd %d] is not registered with this epoll instance.", fd); 160 | }else if(errno == EINVAL){ 161 | LOG("[epoll_fd %d] is not an epoll file descriptor, or [fd %d]is the same as [epoll_fd %d],or the requested operation EPOLL_CTL_ADD is not supported by this interface.", pei->epoll_fd, fd, pei->epoll_fd); 162 | }else if(errno == ENOMEM){ 163 | LOG("memory shorage"); 164 | }else if(errno == ENOSPC){ 165 | LOG("The limit imposed by /proc/sys/fs/epoll/max_user_watches exceeded."); 166 | }else if(errno == EPERM){ 167 | LOG("The target file [fd %d] does not support epoll. " 168 | "It's meaningless to epolling on regular files, read this post through [ http://www.groupsrv.com/linux/about159067.html ].", fd); 169 | } 170 | } 171 | 172 | /* 173 | * Register the given file descriptor with this epoll instance. 174 | * Return: 0 on success, -1 on failure. 175 | * @r: the reactor which uses this policy. 176 | * @fd: the file descriptor to listen. 177 | * @flags: the interested events. 178 | */ 179 | int epoll_add(struct reactor * r, el_socket_t fd, short flags){ 180 | struct epoll_internal * pei; 181 | struct epoll_event e; 182 | int ret; 183 | 184 | assert(r != NULL); 185 | if(r == NULL){ 186 | LOG("r is null!!"); 187 | return (-1); 188 | } 189 | 190 | pei = r->policy_data; 191 | if(pei == NULL){ 192 | LOG("pei is null!!"); 193 | return (-1); 194 | } 195 | 196 | if(pei->n_events >= pei->max_events){ 197 | LOG("resize to %d", pei->max_events << 1); 198 | if(epoll_resize(pei, pei->max_events << 1) == -1){ 199 | LOG("failed on epoll_resize"); 200 | return (-1); 201 | } 202 | } 203 | e.data.fd = fd; 204 | e.events = epoll_setup_mask(flags); 205 | 206 | ret = epoll_ctl(pei->epoll_fd, EPOLL_CTL_ADD, fd, &e); 207 | 208 | /* Error handling*/ 209 | if(ret){ 210 | if(errno == EEXIST){ 211 | LOG("[fd %d]is alredy registered with this epoll instance, retry with EPOLL_CTL_MOD.", fd); 212 | /* retry with EPOLL_CTL_MOD */ 213 | ret = epoll_ctl(pei->epoll_fd, EPOLL_CTL_MOD, fd, &e); 214 | 215 | if(ret == 0) 216 | goto success; 217 | epoll_print_error(pei, fd); 218 | }else{ 219 | epoll_print_error(pei, fd); 220 | } 221 | return (-1); 222 | } 223 | success: 224 | //LOG("success on registering [fd %d] with this epoll instance", fd); 225 | ++pei->n_events; 226 | return (0); 227 | } 228 | 229 | 230 | /* 231 | * Modify the interested events of a fd. 232 | * Return: 0 on success, -1 on failure. 233 | * @r: the reactor which uses this policy. 234 | * @fd: the file descriptor to listen. 235 | * @flags: the interested events. 236 | */ 237 | int epoll_mod(struct reactor * r, el_socket_t fd, short flags){ 238 | struct epoll_internal * pei; 239 | struct epoll_event e; 240 | int ret; 241 | 242 | assert(r != NULL); 243 | if(r == NULL){ 244 | LOG("r is null!!"); 245 | return (-1); 246 | } 247 | 248 | pei = r->policy_data; 249 | if(pei == NULL){ 250 | LOG("pei is null!!"); 251 | return (-1); 252 | } 253 | 254 | e.data.fd = fd; 255 | e.events = epoll_setup_mask(flags); 256 | 257 | ret = epoll_ctl(pei->epoll_fd, EPOLL_CTL_MOD, fd, &e); 258 | 259 | /* Error handling*/ 260 | if(ret){ 261 | epoll_print_error(pei, fd); 262 | return (-1); 263 | } 264 | 265 | return (0); 266 | } 267 | 268 | /* 269 | * Unregister the given file descriptor with this epoll instance. 270 | * Return: -1 on failure, 0 on success. 271 | * @r: the reactor which uses this policy. 272 | * @fd: the file descriptor to remove. 273 | * @flags: the interested events. 274 | */ 275 | int epoll_del(struct reactor * r, el_socket_t fd, short flags){ 276 | struct epoll_internal * pei; 277 | struct epoll_event e; 278 | int ret; 279 | assert(r != NULL); 280 | if(r == NULL){ 281 | LOG("r is null!!"); 282 | return (-1); 283 | } 284 | 285 | pei = r->policy_data; 286 | if(pei == NULL){ 287 | LOG("pei is null!!"); 288 | return (-1); 289 | } 290 | 291 | e.data.fd = fd; 292 | e.events = epoll_setup_mask(flags); 293 | 294 | ret = epoll_ctl(pei->epoll_fd, EPOLL_CTL_DEL, fd, &e); 295 | 296 | if(ret){ 297 | epoll_print_error(pei, fd); 298 | return (-1); 299 | } 300 | 301 | //LOG("success on unregistering [fd %d] with this epoll instance", fd); 302 | --pei->n_events; 303 | return (0); 304 | } 305 | 306 | /* 307 | * Polling the file descriptor via epoll and add active events to the pending_list of the reactor. 308 | * @r: the reactor which uses this policy. 309 | * @timeout: the time after which the select will return. 310 | */ 311 | int epoll_poll(struct reactor * r, struct timeval * timeout){ 312 | int res_flags , nreadys, i; 313 | struct epoll_internal * pei; 314 | struct event * e; 315 | 316 | assert(r != NULL); 317 | 318 | pei = r->policy_data; 319 | 320 | assert(pei != NULL); 321 | 322 | el_lock_unlock(r->lock); 323 | nreadys = epoll_wait(pei->epoll_fd, 324 | pei->events, 325 | pei->n_events, 326 | timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1 ); 327 | el_lock_lock(r->lock); 328 | 329 | for(i = 0; i < nreadys; ++i){ 330 | res_flags = 0; 331 | if(pei->events[i].events & (EPOLLIN | EPOLLPRI)){ 332 | res_flags |= E_READ; 333 | } 334 | if(pei->events[i].events & EPOLLOUT){ 335 | res_flags |= E_WRITE; 336 | } 337 | if(pei->events[i].events & EPOLLERR){ 338 | LOG("got a EPOLLERR event: %s", strerror(errno)); 339 | } 340 | if(res_flags){ 341 | e = event_ht_retrieve(&r->eht, pei->events[i].data.fd); 342 | 343 | assert(e != NULL); 344 | if(e == NULL){ 345 | LOG("the event with [fd %d] is not in the hashtable", pei->events[i].data.fd); 346 | }else{ 347 | reactor_add_to_pending(r, e, res_flags); 348 | } 349 | } 350 | } 351 | 352 | return nreadys; 353 | } 354 | /* Dumps out the internal data of select policy for debugging. */ 355 | void epoll_print(struct reactor * r){ 356 | LOG("empty implementation of epoll_print."); 357 | } 358 | -------------------------------------------------------------------------------- /src/polling_kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* kqueue polling policy */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cheetah/polling_policy.h" 30 | #include "cheetah/includes.h" 31 | #include "cheetah/log.h" 32 | 33 | #define KQUEUE_INIT_EVENT_SIZE 32 34 | 35 | struct kqueue_internal{ 36 | int kqueue_fd; 37 | int nevents; 38 | int max_events; 39 | struct kevent * events; 40 | }; 41 | 42 | /* 43 | * Resize the events to given size. 44 | * Return: -1 on failure, 0 on success. 45 | * @pki: the internal data used by kqueue polling policy. 46 | * @size: the size that we should resize to. 47 | */ 48 | static int kqueue_resize(struct kqueue_internal * pki, int size){ 49 | struct kevent * pke; 50 | assert(pki != NULL); 51 | if(pki == NULL){ 52 | LOG("pki is null!!"); 53 | return (-1); 54 | } 55 | 56 | if((pke = realloc(pki->events, size * sizeof(struct kevent))) == NULL){ 57 | LOG("failed to realloc for events, maybe run out of memory."); 58 | return (-1); 59 | } 60 | 61 | pki->events = pke; 62 | pki->max_events = size; 63 | return (0); 64 | } 65 | 66 | /* 67 | * Create and initialize the internal data used by kqueue polling policy. 68 | * Return value: newly created internal data on success, NULL on failure. 69 | * @r: the reactor which uses this policy. 70 | */ 71 | void * kqueue_init(struct reactor * r){ 72 | struct kqueue_internal * ret; 73 | 74 | assert(r); 75 | if(r == NULL){ 76 | LOG("r is null!!"); 77 | return NULL; 78 | } 79 | 80 | if((ret = malloc(sizeof(struct kqueue_internal))) == NULL){ 81 | LOG("failed to malloc for kqueue_internal"); 82 | return NULL; 83 | } 84 | 85 | memset(ret, 0, sizeof(struct kqueue_internal)); 86 | 87 | if((ret->kqueue_fd = kqueue()) == -1){ 88 | LOG("failed on kqueue(): %s", strerror(errno)); 89 | free(ret); 90 | return NULL; 91 | } 92 | 93 | if(kqueue_resize(ret, KQUEUE_INIT_EVENT_SIZE) == -1){ 94 | LOG("failed on kqueue_resize()"); 95 | close(ret->kqueue_fd); 96 | free(ret); 97 | return NULL; 98 | } 99 | //LOG("allocated %d free events for kqeueu", KQUEUE_INIT_EVENT_SIZE); 100 | return ret; 101 | } 102 | 103 | /* 104 | * Frees up the internal data used by kqueue polling policy. 105 | * @pki: the internal data. 106 | */ 107 | static void kqueue_free(struct kqueue_internal * pki){ 108 | assert(pki != NULL); 109 | if(pki == NULL){ 110 | LOG("pki is null!!"); 111 | return; 112 | } 113 | 114 | if(pki->events){ 115 | free(pki->events); 116 | pki->events = NULL; 117 | } 118 | if(pki->kqueue_fd >= 0){ 119 | close(pki->kqueue_fd); 120 | } 121 | 122 | free(pki); 123 | } 124 | 125 | /* 126 | * Clean up the policy internal data 127 | * @r: the reactor which uses this policy 128 | */ 129 | void kqueue_destroy(struct reactor * r){ 130 | assert(r != NULL); 131 | if(r == NULL){ 132 | LOG("r is null!!"); 133 | return; 134 | } 135 | kqueue_free(r->policy_data); 136 | } 137 | 138 | static inline short kqueue_setup_filter(short flags){ 139 | short ret = 0; 140 | if(flags & E_READ){ 141 | ret = EVFILT_READ; 142 | } 143 | if(flags & E_WRITE){ 144 | ret = EVFILT_WRITE; 145 | } 146 | return ret; 147 | } 148 | 149 | 150 | /* 151 | * Register the given file descriptor with this kqueue instance. 152 | * Return: 0 on success, -1 on failure. 153 | * @r: the reactor which uses this policy. 154 | * @fd: the file descriptor to listen. 155 | * @flags: the interested events. 156 | */ 157 | int kqueue_add(struct reactor * r, el_socket_t fd, short flags){ 158 | struct kqueue_internal * pki; 159 | struct kevent e; 160 | int ret; 161 | uintptr_t ident; 162 | short filter; 163 | ushort action; 164 | 165 | assert(r != NULL); 166 | if(r == NULL){ 167 | LOG("r is null!!"); 168 | return (-1); 169 | } else if(flags & E_EDGE) { 170 | LOG("kqueue does not support edge-triggered mode."); 171 | return (-1); 172 | } 173 | 174 | pki = r->policy_data; 175 | if(pki == NULL){ 176 | LOG("pki is null!!"); 177 | return (-1); 178 | } 179 | 180 | if(pki->nevents >= pki->max_events){ 181 | LOG("resize to %d", pki->max_events << 1); 182 | if(kqueue_resize(pki, pki->max_events << 1) == -1){ 183 | LOG("failed on kqueue_resize"); 184 | return (-1); 185 | } 186 | } 187 | ident = fd; 188 | filter = kqueue_setup_filter(flags); 189 | action = EV_ADD; 190 | EV_SET(&e, ident, filter, action, ((flags & E_ONCE) ? EV_ONESHOT : 0), 0, NULL); 191 | 192 | //LOG("Registering kqueue event: fd %d filter %d action %d flags %d", ident, filter, action, ((flags & E_ONCE) ? EV_ONESHOT : 0)); 193 | ret = kevent(pki->kqueue_fd, &e, 1, NULL, 0, NULL); 194 | 195 | /* Error handling*/ 196 | if(ret){ 197 | LOG("failed to add event to kqueue: %s", strerror(errno)); 198 | return (-1); 199 | } 200 | //LOG("Registered fd %d for envets %d", fd, flags); 201 | ++pki->nevents; 202 | return (0); 203 | } 204 | 205 | 206 | 207 | /* 208 | * Modify the interested events of a fd. 209 | * Return: 0 on success, -1 on failure. 210 | * @r: the reactor which uses this policy. 211 | * @fd: the file descriptor to listen. 212 | * @flags: the interested events. 213 | */ 214 | int kqueue_mod(struct reactor * r, el_socket_t fd, short flags){ 215 | struct kqueue_internal * pki; 216 | struct kevent e; 217 | int ret; 218 | uintptr_t ident; 219 | short filter; 220 | ushort action; 221 | 222 | assert(r != NULL); 223 | if(r == NULL){ 224 | LOG("r is null!!"); 225 | return (-1); 226 | } else if(flags & E_EDGE) { 227 | LOG("kqueue does not support edge-triggered mode."); 228 | return (-1); 229 | } 230 | 231 | pki = r->policy_data; 232 | if(pki == NULL){ 233 | LOG("pki is null!!"); 234 | return (-1); 235 | } 236 | 237 | ident = fd; 238 | filter = kqueue_setup_filter(flags); 239 | action = EV_ADD; 240 | /* EV_ADD will override the events if the fd is already registered */ 241 | EV_SET(&e, ident, filter, action, ((flags & E_ONCE) ? EV_ONESHOT : 0), 0, NULL); 242 | 243 | ret = kevent(pki->kqueue_fd, &e, 1, NULL, 0, NULL); 244 | 245 | /* Error handling*/ 246 | if(ret){ 247 | LOG("failed to modify the event: %s", strerror(errno)); 248 | return (-1); 249 | } 250 | return (0); 251 | } 252 | 253 | /* 254 | * Unregister the given file descriptor with this kqueue instance. 255 | * Return: -1 on failure, 0 on success. 256 | * @r: the reactor which uses this policy. 257 | * @fd: the file descriptor to remove. 258 | * @flags: the interested events. 259 | */ 260 | int kqueue_del(struct reactor * r, el_socket_t fd, short flags){ 261 | struct kqueue_internal * pki; 262 | struct kevent e; 263 | int ret; 264 | uintptr_t ident; 265 | short filter; 266 | ushort action; 267 | 268 | assert(r != NULL); 269 | if(r == NULL){ 270 | LOG("r is null!!"); 271 | return (-1); 272 | } 273 | 274 | pki = r->policy_data; 275 | if(pki == NULL){ 276 | LOG("pki is null!!"); 277 | return (-1); 278 | } 279 | 280 | ident = fd; 281 | filter = kqueue_setup_filter(flags); 282 | action = EV_DELETE; 283 | EV_SET(&e, ident, filter, action, 0, 0, NULL); 284 | 285 | ret = kevent(pki->kqueue_fd, &e, 1, NULL, 0, NULL); 286 | 287 | if(ret){ 288 | LOG("failed to delete event from kqueue: %s", strerror(errno)); 289 | return (-1); 290 | } 291 | 292 | --pki->nevents; 293 | return (0); 294 | } 295 | 296 | /* 297 | * Polling the file descriptor via kqueue and add active events to the pending_list of the reactor. 298 | * @r: the reactor which uses this policy. 299 | * @timeout: the time after which the select will return. 300 | */ 301 | int kqueue_poll(struct reactor * r, struct timeval * timeout){ 302 | int res_flags , nreadys, i; 303 | struct kqueue_internal * pki; 304 | struct event * e; 305 | struct timespec t; 306 | 307 | assert(r != NULL); 308 | 309 | pki = r->policy_data; 310 | 311 | assert(pki != NULL); 312 | 313 | if (timeout) { 314 | t.tv_sec = timeout->tv_sec; 315 | t.tv_nsec = timeout->tv_usec * 1000; 316 | } 317 | el_lock_unlock(r->lock); 318 | nreadys = kevent(pki->kqueue_fd, NULL, 0, 319 | pki->events, pki->nevents, 320 | timeout ? &t : NULL); 321 | el_lock_lock(r->lock); 322 | //LOG("kevent: %d events are ready", nreadys); 323 | for(i = 0; i < nreadys; ++i){ 324 | res_flags = 0; 325 | if(pki->events[i].filter == EVFILT_READ){ 326 | res_flags = E_READ; 327 | } 328 | if(pki->events[i].filter == EVFILT_WRITE){ 329 | res_flags = E_WRITE; 330 | } 331 | if(pki->events[i].flags == EV_ERROR){ 332 | LOG("kevent's EV_ERROR flag is set: %s", strerror(errno)); 333 | } 334 | if(res_flags){ 335 | e = event_ht_retrieve(&r->eht, pki->events[i].ident); 336 | 337 | assert(e != NULL); 338 | if(e == NULL){ 339 | LOG("the event with [fd %d] is not in the hashtable", pki->events[i].ident); 340 | }else{ 341 | reactor_add_to_pending(r, e, res_flags); 342 | } 343 | } 344 | } 345 | 346 | return nreadys; 347 | } 348 | /* Dumps out the internal data of kqueue policy for debugging. */ 349 | void kqueue_print(struct reactor * r){ 350 | LOG("empty implementation of kqueue_print."); 351 | } 352 | -------------------------------------------------------------------------------- /src/polling_policy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include "cheetah/config.h" 24 | #include "cheetah/polling_policy.h" 25 | #ifdef HAVE_EPOLL 26 | #include "polling_epoll.c" 27 | #endif 28 | #ifdef HAVE_KQUEUE 29 | #include "polling_kqueue.c" 30 | #endif 31 | #ifdef HAVE_POLL 32 | #include "polling_poll.c" 33 | #endif 34 | #ifdef HAVE_SELECT 35 | #include "polling_select.c" 36 | #endif 37 | struct polling_policy polling_policies[] = { 38 | #ifdef HAVE_EPOLL 39 | {"epoll", 40 | epoll_init, 41 | epoll_add, 42 | epoll_mod, 43 | epoll_del, 44 | epoll_poll, 45 | epoll_destroy, 46 | epoll_print}, 47 | #endif 48 | #ifdef HAVE_KQUEUE 49 | {"kqueue", 50 | kqueue_init, 51 | kqueue_add, 52 | kqueue_mod, 53 | kqueue_del, 54 | kqueue_poll, 55 | kqueue_destroy, 56 | kqueue_print}, 57 | #endif 58 | #ifdef HAVE_POLL 59 | {"poll", 60 | poll_init, 61 | poll_add, 62 | poll_mod, 63 | poll_del, 64 | poll_poll, 65 | poll_destroy, 66 | poll_print}, 67 | #endif 68 | #ifdef HAVE_SELECT 69 | {"select", 70 | select_init, 71 | select_add, 72 | select_mod, 73 | select_del, 74 | select_poll, 75 | select_destroy, 76 | select_print}, 77 | #endif 78 | {NULL} 79 | }; -------------------------------------------------------------------------------- /src/polling_poll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* poll polling policy */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cheetah/polling_policy.h" 30 | #include "cheetah/includes.h" 31 | #include "cheetah/log.h" 32 | 33 | 34 | #define POLL_INIT_EVENT_SIZE 32 35 | 36 | struct poll_internal{ 37 | /* the number of pollfds that fds_in pointed to */ 38 | int max_events; 39 | /* the number of pollfds that we have added to fds_in */ 40 | int n_events; 41 | /* 42 | * Indicates whether we need to realloc the fds to match 43 | * max_events, since we might have changed the 44 | * size of memory that fds_in pointed to. 45 | */ 46 | int need_realoc; 47 | /* 48 | * Indicates whether we need memcpy from fds_in to fds. 49 | */ 50 | int need_rememcp; 51 | struct pollfd *fds; 52 | /* 53 | * In multithreaded environment, we may encounter situation that 54 | * main thread is polling while another thread trying to modify the fds. 55 | * So we introduce the field to avoid this kind of race condition. 56 | * If we are in singlethreaded environment, we simply point fds to fds_in. 57 | */ 58 | struct pollfd *fds_in; 59 | }; 60 | 61 | /* 62 | * Resize the fds to given size. 63 | * Return: -1 on failure, 0 on success. 64 | * @ppi: the internal data used by poll polling policy. 65 | * @size: the size that we should resize to. 66 | */ 67 | static int poll_resize(struct poll_internal * ppi, int size){ 68 | struct pollfd * pfds; 69 | assert(ppi != NULL); 70 | if(ppi == NULL){ 71 | LOG("ppi is null!!"); 72 | return (-1); 73 | } 74 | 75 | if((pfds = realloc(ppi->fds_in, size * sizeof(struct pollfd))) == NULL){ 76 | LOG("failed to realloc for fds, maybe run out of memory."); 77 | return (-1); 78 | } 79 | 80 | ppi->fds_in = pfds; 81 | ppi->max_events = size; 82 | ppi->need_realoc = 1; 83 | ppi->need_rememcp = 1; 84 | return (0); 85 | } 86 | 87 | /* 88 | * Create and initialize the internal data used by poll polling policy. 89 | * Return value: newly created internal data on success, NULL on failure. 90 | * @r: the reactor which uses this policy. 91 | */ 92 | void * poll_init(struct reactor * r){ 93 | struct poll_internal * ret; 94 | assert(r != NULL); 95 | if(r == NULL){ 96 | LOG("r is null!!"); 97 | return NULL; 98 | } 99 | 100 | if((ret = malloc(sizeof(struct poll_internal))) == NULL){ 101 | LOG("failed to malloc for poll_internal"); 102 | return NULL; 103 | } 104 | 105 | memset(ret, 0, sizeof(struct poll_internal)); 106 | 107 | if(poll_resize(ret, POLL_INIT_EVENT_SIZE) == -1){ 108 | LOG("failed on poll_resize"); 109 | free(ret); 110 | return NULL; 111 | } 112 | 113 | return ret; 114 | } 115 | 116 | /* 117 | * Frees up the internal data used by poll polling policy. 118 | * @pei: the internal data. 119 | */ 120 | static void poll_free(struct poll_internal * ppi){ 121 | assert(ppi != NULL); 122 | if(ppi == NULL){ 123 | LOG("ppi is null!!"); 124 | return; 125 | } 126 | 127 | if(ppi->fds){ 128 | free(ppi->fds); 129 | ppi->fds = NULL; 130 | } 131 | if(ppi->fds_in){ 132 | free(ppi->fds_in); 133 | ppi->fds_in = NULL; 134 | } 135 | 136 | free(ppi); 137 | } 138 | 139 | /* 140 | * Clean up the policy internal data 141 | * @r: the reactor which uses this policy 142 | */ 143 | void poll_destroy(struct reactor * r){ 144 | assert(r != NULL); 145 | if(r == NULL){ 146 | LOG("r is null!!"); 147 | return; 148 | } 149 | poll_free(r->policy_data); 150 | } 151 | 152 | static inline int poll_setup_mask(short flags){ 153 | int ret = 0; 154 | if(flags & E_READ){ 155 | ret |= POLLIN | POLLPRI; 156 | } 157 | if(flags & E_WRITE){ 158 | ret |= POLLOUT; 159 | } 160 | return ret; 161 | } 162 | /* 163 | * Add the given fd to the fds of the internal data of poll polling policy. 164 | * If the fd in already in the fds, update the events according to the flags. 165 | * Resize the fds if nessesary. 166 | * Return: 0 on success, -1 on failure. 167 | * @r: the reactor which uses this policy. 168 | * @fd: the file descriptor to listen. 169 | * @flags: the interested events. 170 | */ 171 | int poll_add(struct reactor * r, el_socket_t fd, short flags){ 172 | struct poll_internal * ppi; 173 | int i; 174 | assert(r != NULL); 175 | if(r == NULL){ 176 | LOG("r is null!!"); 177 | return (-1); 178 | } 179 | 180 | ppi = r->policy_data; 181 | if(ppi == NULL){ 182 | LOG("ppi is null!!"); 183 | return (-1); 184 | } 185 | 186 | for(i = 0; i < ppi->n_events; ++i){ 187 | if(fd == ppi->fds_in[i].fd){ 188 | /* Override the existing event */ 189 | ppi->fds_in[i].events = poll_setup_mask(flags); 190 | return (0); 191 | } 192 | } 193 | if(ppi->n_events >= ppi->max_events){ 194 | LOG("resize to %d", ppi->max_events << 1); 195 | if(poll_resize(ppi, ppi->max_events << 1) == -1){ 196 | LOG("failed on poll_resize"); 197 | return (-1); 198 | } 199 | } 200 | ppi->fds_in[i].fd = fd; 201 | ppi->fds_in[i].events = poll_setup_mask(flags); 202 | ppi->need_rememcp = 1;; 203 | ++ppi->n_events; 204 | 205 | return (0); 206 | } 207 | 208 | 209 | 210 | /* 211 | * Modify the events according to the flags. 212 | * Resize the fds if nessesary. 213 | * Return: 0 on success, -1 on failure. 214 | * @r: the reactor which uses this policy. 215 | * @fd: the file descriptor to listen. 216 | * @flags: the interested events. 217 | */ 218 | int poll_mod(struct reactor * r, el_socket_t fd, short flags){ 219 | struct poll_internal * ppi; 220 | int i; 221 | assert(r != NULL); 222 | if(r == NULL){ 223 | LOG("r is null!!"); 224 | return (-1); 225 | } 226 | 227 | ppi = r->policy_data; 228 | if(ppi == NULL){ 229 | LOG("ppi is null!!"); 230 | return (-1); 231 | } 232 | 233 | for(i = 0; i < ppi->n_events; ++i){ 234 | if(fd == ppi->fds_in[i].fd){ 235 | /* Override the existing event */ 236 | ppi->fds_in[i].events = poll_setup_mask(flags); 237 | return (0); 238 | } 239 | } 240 | 241 | LOG("fd[%d] is not in the poll fdset.", fd); 242 | return (-1); 243 | } 244 | 245 | static inline void poll_swap_pollfd(struct pollfd * lhs, struct pollfd * rhs){ 246 | struct pollfd t; 247 | t = *lhs; 248 | *lhs = *rhs; 249 | *rhs = t; 250 | } 251 | /* 252 | * Remove the given file descriptor from the listening pollfds. 253 | * Return value: -1 on failure, 0 on success. 254 | * @r: the reactor which uses this policy. 255 | * @fd: the file descriptor to remove. 256 | * @flags: the interested events. 257 | */ 258 | int poll_del(struct reactor * r, el_socket_t fd, short flags){ 259 | struct poll_internal * ppi; 260 | int i; 261 | 262 | assert(r != NULL); 263 | if(r == NULL){ 264 | LOG("r is null!!"); 265 | return (-1); 266 | } 267 | ppi = r->policy_data; 268 | 269 | assert(ppi != NULL); 270 | if(ppi == NULL){ 271 | LOG("policy internal data is needed but null provided."); 272 | return (-1); 273 | } 274 | for(i = 0; i < ppi->n_events; ++i){ 275 | if(ppi->fds_in[i].fd == fd){ 276 | poll_swap_pollfd(&ppi->fds_in[i], &ppi->fds_in[ppi->n_events - 1]); 277 | ppi->need_rememcp = 1; 278 | --ppi->n_events; 279 | return (0); 280 | } 281 | } 282 | 283 | LOG("the [fd %d] in not in the pollfds", fd); 284 | 285 | return (-1); 286 | } 287 | /* 288 | * Polling the file descriptors via poll and add active events to the pending_list of the reactor. 289 | * @r: the reactor which uses this policy. 290 | * @timeout: the time after which the poll will return. 291 | */ 292 | int poll_poll(struct reactor * r, struct timeval * timeout){ 293 | int res_flags , nreadys; 294 | struct poll_internal * ppi; 295 | struct event * e; 296 | int i; 297 | 298 | assert(r != NULL); 299 | if(r == NULL){ 300 | LOG_EXIT(1, "r is null!!"); 301 | } 302 | 303 | ppi = r->policy_data; 304 | 305 | assert(ppi != NULL); 306 | if(ppi == NULL){ 307 | LOG_EXIT(1, "ppi is null!!"); 308 | } 309 | 310 | if(r->lock){ 311 | /* 312 | * We are in multithreaded enviroment, 313 | * do whatever we have to do. 314 | */ 315 | if(ppi->need_realoc){ 316 | if((ppi->fds = realloc(ppi->fds, ppi->max_events * sizeof(struct pollfd))) == NULL){ 317 | LOG_EXIT(1, "failed to realloc for ppi->fds"); 318 | } 319 | ppi->need_realoc = 0; 320 | } 321 | if(ppi->need_rememcp){ 322 | memcpy(ppi->fds, ppi->fds_in, ppi->max_events * sizeof(struct pollfd)); 323 | ppi->need_rememcp = 0; 324 | } 325 | }else{ 326 | /* No need to realloc and rememcpy since we are in single-threaeded environment. */ 327 | ppi->fds = ppi->fds_in; 328 | } 329 | 330 | el_lock_unlock(r->lock); 331 | nreadys = poll(ppi->fds, 332 | ppi->n_events, 333 | timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1); 334 | el_lock_lock(r->lock); 335 | 336 | if(nreadys){ 337 | for(i = 0; i < ppi->n_events; ++i){ 338 | res_flags = 0; 339 | if(ppi->fds[i].revents & (POLLIN | POLLPRI)){ 340 | res_flags |= E_READ; 341 | } 342 | if(ppi->fds[i].revents & POLLOUT){ 343 | res_flags |= E_WRITE; 344 | } 345 | if(ppi->fds[i].revents & POLLNVAL){ 346 | LOG("[fd %d] is invalid!", ppi->fds[i].fd); 347 | } 348 | if(ppi->fds[i].revents & POLLERR){ 349 | LOG("got a POLLERR event: %s", strerror(errno)); 350 | } 351 | 352 | if(res_flags){ 353 | e = event_ht_retrieve(&r->eht, ppi->fds[i].fd); 354 | 355 | assert(e != NULL); 356 | if(e == NULL){ 357 | LOG("the event with [fd %d] is not in the hashtable", ppi->fds[i].fd); 358 | }else{ 359 | reactor_add_to_pending(r, e, res_flags); 360 | } 361 | } 362 | } 363 | } 364 | return nreadys; 365 | } 366 | 367 | /* Dumps out the internal data of poll policy for debugging. */ 368 | void poll_print(struct reactor * r){ 369 | int i; 370 | struct poll_internal * ppi = r->policy_data; 371 | 372 | assert(r != NULL); 373 | 374 | ppi = r->policy_data; 375 | 376 | printf("max_events:%d\n", ppi->max_events); 377 | printf("n_events:%d\n", ppi->n_events); 378 | printf("fds:%p\n", ppi->fds); 379 | printf("fds_in:%p\n", ppi->fds_in); 380 | for(i = 0; i < ppi->n_events; ++i){ 381 | printf("[fd %d]: ", ppi->fds[i].fd); 382 | if(ppi->fds[i].revents & (POLLIN | POLLPRI)){ 383 | printf("set for reading "); 384 | } 385 | if(ppi->fds[i].revents & POLLOUT){ 386 | printf("set for writing "); 387 | } 388 | printf("\n"); 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/polling_select.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* select polling policy */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cheetah/utility.h" 30 | #include "cheetah/polling_policy.h" 31 | #include "cheetah/event.h" 32 | 33 | #ifdef WIN32 34 | #include 35 | #include 36 | #else 37 | #include 38 | #endif 39 | 40 | typedef unsigned long FD_MASK; 41 | 42 | #define NBPB 8 /* Number of bits per byte */ 43 | #define NBPM (sizeof(FD_MASK) * NBPB) /* Number of bits per mask */ 44 | #define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))/* Rounding up division */ 45 | #define FD_TO_NUM_OF_INT(fd) DIV_ROUND_UP(fd, NBPM)/* Number of int needed to hold the given fd */ 46 | #define FD_TO_BYTES(fd) (FD_TO_NUM_OF_INT(fd) * sizeof(unsigned long))/* Number of bytes needed to hold the given fd */ 47 | #define FD_INIT_BYTES 32 /* Initial fd_set size */ 48 | struct select_internal{ 49 | el_socket_t maxfd; 50 | int bytes_avail; 51 | fd_set *readset_in; 52 | fd_set *writeset_in; 53 | fd_set *readset_out; 54 | fd_set *writeset_out; 55 | }; 56 | 57 | /* 58 | * Resize the fd_sets to given size. 59 | * Return value: 0 on success, -1 on failure. 60 | * @psi: the internal data used by select. 61 | * @bytes: the desired size in byte. 62 | */ 63 | static int select_resize(struct select_internal * psi, int bytes){ 64 | fd_set * preadset_in = NULL; 65 | fd_set * pwriteset_in = NULL; 66 | fd_set * preadset_out = NULL; 67 | fd_set * pwriteset_out = NULL; 68 | 69 | assert(psi != NULL); 70 | if(psi == NULL){ 71 | LOG("psi is null!!"); 72 | return (-1); 73 | } 74 | if((preadset_in = realloc(psi->readset_in, bytes)) == NULL){ 75 | return (-1); 76 | } 77 | if((pwriteset_in = realloc(psi->writeset_in, bytes)) == NULL){ 78 | free(preadset_in); 79 | return (-1); 80 | } 81 | if((preadset_out= realloc(psi->readset_out, bytes)) == NULL){ 82 | free(preadset_in); 83 | free(pwriteset_in); 84 | return (-1); 85 | } 86 | if((pwriteset_out = realloc(psi->writeset_out, bytes)) == NULL){ 87 | free(preadset_in); 88 | free(pwriteset_in); 89 | free(preadset_out); 90 | return (-1); 91 | } 92 | psi->readset_in = preadset_in; 93 | psi->writeset_in = pwriteset_in; 94 | psi->readset_out = preadset_out; 95 | psi->writeset_out = pwriteset_out; 96 | /* 97 | * Zeros the newly appended memory. 98 | * We don't have to zeros the outs, because they will copy from the ins. 99 | */ 100 | memset(psi->readset_in + psi->bytes_avail, 0, bytes - psi->bytes_avail); 101 | memset(psi->writeset_in + psi->bytes_avail, 0, bytes - psi->bytes_avail); 102 | /* */ 103 | 104 | psi->bytes_avail = bytes; 105 | 106 | return (0); 107 | } 108 | 109 | /* 110 | * Free up the internal data used by select. 111 | * Return value: 0 for success. 112 | * @psi: the internal data used by select. 113 | */ 114 | static int select_free(struct select_internal * psi){ 115 | assert(psi != NULL); 116 | 117 | if(psi->readset_in) 118 | free(psi->readset_in); 119 | if(psi->writeset_in) 120 | free(psi->writeset_in); 121 | if(psi->readset_out) 122 | free(psi->readset_out); 123 | if(psi->writeset_out) 124 | free(psi->writeset_out); 125 | psi->readset_in = psi->writeset_in = NULL; 126 | psi->readset_out = psi->writeset_out = NULL; 127 | free(psi); 128 | LOG("done freeing policy_select internal data."); 129 | return (0); 130 | } 131 | 132 | /* 133 | * Create and initialize the internal data used by select polling policy. 134 | * Return value: newly created internal data on success, NULL on failure. 135 | * @r: the reactor which uses this policy. 136 | */ 137 | void * select_init(struct reactor * r){ 138 | struct select_internal * ret; 139 | 140 | assert(r != NULL); 141 | if(r == NULL){ 142 | LOG("r is null!!"); 143 | return NULL; 144 | } 145 | 146 | if((ret = malloc(sizeof(struct select_internal))) == NULL){ 147 | return NULL; 148 | } 149 | 150 | memset(ret, 0, sizeof(struct select_internal)); 151 | 152 | if(select_resize(ret, FD_TO_BYTES(FD_INIT_BYTES + 1)) == -1){ 153 | select_free(ret); 154 | return NULL; 155 | } 156 | 157 | return ret; 158 | } 159 | 160 | /* 161 | * Add the given file descriptor to the listening fd_set. 162 | * Return value: 0 on success, -1 on failure. 163 | * @r: the reactor which uses this policy. 164 | * @fd: the file descriptor to listen. 165 | * @flags: the interested events. 166 | */ 167 | int select_add(struct reactor * r, el_socket_t fd, short flags){ 168 | struct select_internal * psi; 169 | assert(r != NULL); 170 | if(r == NULL){ 171 | LOG("r is null!!"); 172 | return (-1); 173 | } 174 | LOG("Adding a event with fd %d and flags %d", fd, flags); 175 | 176 | psi = r->policy_data; 177 | if(psi == NULL){ 178 | LOG("pei is null!!"); 179 | return (-1); 180 | } 181 | if(fd > psi->maxfd){ 182 | int new_avail = psi->bytes_avail; 183 | 184 | //Doubles up to accommodate the fd 185 | while(new_avail < FD_TO_BYTES(fd + 1)) 186 | new_avail <<= 1; 187 | 188 | if(new_avail != psi->bytes_avail){ 189 | if(select_resize(psi, new_avail) == -1){ 190 | LOG("memory shortage."); 191 | return (-1); 192 | } 193 | } 194 | //maintain the maximum fd for the select syscall 195 | psi->maxfd = fd; 196 | } 197 | 198 | if(flags & E_READ){ 199 | FD_SET(fd, psi->readset_in); 200 | } 201 | if(flags & E_WRITE){ 202 | FD_SET(fd, psi->writeset_in); 203 | } 204 | if(flags & E_EDGE){ 205 | LOG("ET is not supported by select polling policy."); 206 | } 207 | LOG("Added a event with fd %d and flags %d", fd, flags); 208 | return (0); 209 | } 210 | 211 | /* 212 | * Modify the interested events of a fd. 213 | * Return value: 0 on success, -1 on failure. 214 | * @r: the reactor which uses this policy. 215 | * @fd: the file descriptor to listen. 216 | * @flags: the interested events. 217 | */ 218 | int select_mod(struct reactor * r, el_socket_t fd, short flags){ 219 | struct select_internal * psi; 220 | assert(r != NULL); 221 | if(r == NULL){ 222 | LOG("r is null!!"); 223 | return (-1); 224 | } 225 | LOG("Adding a event with fd %d and flags %d", fd, flags); 226 | 227 | psi = r->policy_data; 228 | if(psi == NULL){ 229 | LOG("pei is null!!"); 230 | return (-1); 231 | } 232 | 233 | if(fd > psi->maxfd){ 234 | LOG("fd[%d] is not in the select set", fd); 235 | return (-1); 236 | } 237 | 238 | if(flags & E_READ){ 239 | FD_SET(fd, psi->readset_in); 240 | } else { 241 | FD_CLR(fd, psi->readset_in); 242 | } 243 | 244 | if(flags & E_WRITE){ 245 | FD_SET(fd, psi->writeset_in); 246 | } else { 247 | FD_CLR(fd, psi->writeset_in); 248 | } 249 | 250 | if(flags & E_EDGE){ 251 | LOG("ET is not supported by select polling policy."); 252 | } 253 | LOG("Modified the event with fd %d and flags %d", fd, flags); 254 | return (0); 255 | } 256 | /* 257 | * Remove the given file descriptor from the listening fd_set. 258 | * Return value: -1 on failure, 0 on success. 259 | * @r: the reactor which uses this policy. 260 | * @fd: the file descriptor to remove. 261 | * @flags: the interested events. 262 | */ 263 | int select_del(struct reactor * r, el_socket_t fd, short flags){ 264 | struct select_internal * psi; 265 | 266 | assert(r != NULL); 267 | LOG("Removing a event with fd %d and flags %d in the fd_set", fd, flags); 268 | 269 | psi = r->policy_data; 270 | 271 | assert(psi != NULL); 272 | if(psi == NULL){ 273 | LOG("policy internal data is needed but null provided."); 274 | return (-1); 275 | } 276 | if(fd > psi->maxfd) return(0); 277 | 278 | if(flags & E_READ){ 279 | FD_CLR(fd, psi->readset_in); 280 | } 281 | if(flags & E_WRITE){ 282 | FD_CLR(fd, psi->writeset_in); 283 | } 284 | LOG("Removed a event with fd %d and flags %d in the fd_set", fd, flags); 285 | return (0); 286 | } 287 | 288 | /* 289 | * Polling the file descriptors via select and add active events to the pending_list of the reactor. 290 | * @r: the reactor which uses this policy. 291 | * @timeout: the time after which the select will return. 292 | */ 293 | int select_poll(struct reactor * r, struct timeval * timeout){ 294 | int res_flags , nreadys, fd; 295 | struct select_internal * psi; 296 | struct event * e; 297 | 298 | assert(r != NULL); 299 | if(r == NULL){ 300 | LOG_EXIT(1, "r is null!!"); 301 | } 302 | 303 | psi = r->policy_data; 304 | 305 | assert(psi != NULL); 306 | 307 | memcpy(psi->readset_out, psi->readset_in, psi->bytes_avail); 308 | memcpy(psi->writeset_out, psi->writeset_in, psi->bytes_avail); 309 | 310 | el_lock_unlock(r->lock); 311 | nreadys = select(psi->maxfd + 1, psi->readset_out, psi->writeset_out, NULL, timeout); 312 | el_lock_lock(r->lock); 313 | 314 | if(nreadys){ 315 | for(fd = 0; fd <= psi->maxfd; ++fd){ 316 | res_flags = 0; 317 | if(FD_ISSET(fd, psi->readset_out) && FD_ISSET(fd, psi->readset_in))res_flags |= E_READ;; 318 | if(FD_ISSET(fd, psi->writeset_out) && FD_ISSET(fd, psi->readset_in))res_flags |= E_WRITE; 319 | 320 | if(res_flags){ 321 | e = event_ht_retrieve(&r->eht, fd); 322 | 323 | assert(e != NULL); 324 | if(e == NULL){ 325 | LOG("the event with [fd %d] is not in the hashtable", fd); 326 | }else{ 327 | reactor_add_to_pending(r, e, res_flags); 328 | } 329 | } 330 | } 331 | } 332 | 333 | 334 | return nreadys; 335 | } 336 | 337 | /* 338 | * Clean up the policy internal data 339 | * @r: the reactor which uses this policy 340 | */ 341 | void select_destroy(struct reactor * r){ 342 | assert(r != NULL); 343 | 344 | select_free(r->policy_data); 345 | } 346 | 347 | /* Dumps out the internal data of select policy for debugging. */ 348 | void select_print(struct reactor * r){ 349 | int i; 350 | struct select_internal * psi = r->policy_data; 351 | 352 | assert(r != NULL); 353 | 354 | psi = r->policy_data; 355 | 356 | printf("maxfd:%d\n", psi->maxfd); 357 | printf("bytes_avail:%d\n", psi->bytes_avail); 358 | for(i = 0; i <= psi->maxfd; ++i){ 359 | printf("%d", i); 360 | if(FD_ISSET(i, psi->readset_in)){ 361 | printf(" set for reading"); 362 | } 363 | if(FD_ISSET(i, psi->writeset_in)){ 364 | printf(" set for writing"); 365 | } 366 | printf("\n"); 367 | } 368 | } -------------------------------------------------------------------------------- /src/reactor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "cheetah/reactor.h" 29 | #include "cheetah/event.h" 30 | #include "cheetah/list.h" 31 | #include "cheetah/log.h" 32 | #include "cheetah/polling_policy.h" 33 | #include "cheetah/utility.h" 34 | 35 | extern struct polling_policy polling_policies[]; 36 | 37 | /* 38 | * Initialize the reactor. 39 | * @r: the reactor to be initialized. 40 | * @policy_name: the name of polling policy. 41 | * @in_mt: indicates whether this application is used in multithreaded environment. 42 | * @handle_sig: indicates whether the reactor handles signal events. 43 | * @handle_timer: indicates whether the reactor handles timer events. 44 | */ 45 | static void reactor_init_(struct reactor * r, const char * policy_name, int in_mt, int handle_sig, int handle_timer); 46 | 47 | /* 48 | * Dequeue a event from event list. 49 | * Return: the dequeued event or null if the list is empty. 50 | * @r: the reactor. 51 | */ 52 | static struct event * reactor_dequeue_event(struct reactor * r); 53 | 54 | /* 55 | * Initialize signal events handling mechanism. 56 | * Return: 0 on success, -1 on failure. 57 | * @r: the related reactor. 58 | */ 59 | static int reacotr_signal_init(struct reactor * r); 60 | 61 | /* 62 | * Initialize timer events handling mechanism. 63 | * Return: 0 on success, -1 on failure. 64 | * @r: the related reactor. 65 | */ 66 | int reactor_timer_init(struct reactor * r); 67 | 68 | /* 69 | * The signal event callback used to call corresponding signal event callbacks. 70 | */ 71 | static void reactor_signal_callback(el_socket_t fd, short res_flags, void *arg); 72 | 73 | static void reactor_signal_callback(el_socket_t fd, short res_flags, void *arg){ 74 | struct reactor * r; 75 | struct event * e; 76 | struct signal_internal * psi; 77 | int sig; 78 | 79 | sig = 0; 80 | r = arg; 81 | psi = r->psi; 82 | assert(r != NULL && psi != NULL); 83 | 84 | while(read(fd, &sig, 1) > 0){ 85 | e = psi->sigevents[sig]; 86 | 87 | assert(e != NULL && e->callback != NULL); 88 | 89 | e->callback(e->fd, E_SIGNAL, e->callback_arg); 90 | } 91 | } 92 | 93 | static void reactor_handle_timer(struct reactor * r){ 94 | struct event * e; 95 | 96 | assert(r != NULL); 97 | 98 | while(timerheap_top_expired(r)){ 99 | e = timerheap_get_top(r); 100 | 101 | assert(e != NULL); 102 | 103 | if(e->ev_flags & E_ONCE && e->timerheap_idx != E_OUT_OF_TIMERHEAP){ 104 | if (timerheap_remove_event(r, e) == -1) { 105 | LOG("failed to remove timer event from the heap"); 106 | } else { 107 | e->ev_flags &= ~E_IN_REACTOR; 108 | } 109 | } else if(!(e->ev_flags & E_ONCE) && e->timerheap_idx != E_OUT_OF_TIMERHEAP){ 110 | timerheap_reset_timer(r, e); 111 | } 112 | 113 | el_lock_unlock(r->lock); 114 | e->callback(e->fd, e->res_flags, e->callback_arg); 115 | el_lock_lock(r->lock); 116 | } 117 | } 118 | /* 119 | * The default callback got called after reactor being waked up. 120 | */ 121 | static void reactor_waked_up(el_socket_t fd, short res_flags, void *arg){ 122 | //LOG("woke up."); 123 | int n; 124 | char buf[1024]; 125 | while((n = read(fd, buf, sizeof(buf))) > 0); 126 | } 127 | 128 | /* 129 | * Wake up the polling thread. 130 | * @r: the reactor to wake up 131 | */ 132 | static void reactor_wake_up(struct reactor * r){ 133 | assert(r != NULL); 134 | 135 | char octet = 0; 136 | int n = 0; 137 | 138 | n = write(r->pipe[1], &octet, sizeof(octet)); 139 | assert(n > 0); 140 | } 141 | 142 | void reactor_get_out(struct reactor * r){ 143 | el_lock_lock(r->lock); 144 | r->out = 1; 145 | reactor_wake_up(r); 146 | el_lock_unlock(r->lock); 147 | } 148 | 149 | static void reactor_init_(struct reactor * r, const char * policy_name, int in_mt, int handle_sig, int handle_timer){ 150 | assert(r != NULL); 151 | 152 | memset(r, 0, sizeof(struct reactor)); 153 | if(event_ht_init(&r->eht, 0.5) == -1){ 154 | LOG_EXIT(1, "failed to initialize the hash table of events."); 155 | } 156 | 157 | INIT_LIST_HEAD(&r->event_list); 158 | INIT_LIST_HEAD(&r->pending_list); 159 | 160 | struct polling_policy * p = polling_policies; 161 | /* Find the corresponding policy by looping through predefined policies. */ 162 | while(p){ 163 | /* 164 | * We choose the first one in the array if policy_name is null, 165 | * since we have sorted the array by the performance of these polling mechanisms. 166 | */ 167 | if(policy_name == NULL || !strcmp(p->name, policy_name)){ 168 | r->policy = malloc(sizeof(*p)); 169 | if(r->policy == NULL){ 170 | LOG_EXIT(1, "failed to allocate memory for policy."); 171 | } 172 | memcpy(r->policy, p, sizeof(*p)); 173 | break; 174 | } 175 | p++; 176 | } 177 | if(p == NULL){ 178 | LOG_EXIT(1, "polling policy: %s is not supported.", policy_name); 179 | } 180 | 181 | LOG("using %s polling policy.", p->name); 182 | /* Policy internal initialization. */ 183 | r->policy_data = r->policy->init(r); 184 | if(r->policy_data == NULL){ 185 | LOG_EXIT(1, "failed to initialize polling policy[%s].", policy_name); 186 | } 187 | 188 | if(in_mt){ 189 | /* We only use lock in multithreaded environment to reduce overhead. */ 190 | r->lock = malloc(sizeof(el_lock)); 191 | } 192 | 193 | /* If the lock is null, locking functions will be no-ops */ 194 | el_lock_init(r->lock); 195 | r->out = 0; 196 | if(el_create_pipe(r->pipe) == -1){ 197 | LOG_EXIT(1, "failed to create informing pipe."); 198 | } 199 | /* set the pipe to nonblocking mode */ 200 | if(el_set_nonblocking(r->pipe[0]) < 0 || el_set_nonblocking(r->pipe[1]) < 0){ 201 | LOG_EXIT(1, "failed to set the pipe to nonblocking mode."); 202 | } 203 | 204 | //set up the informer 205 | event_set(&r->pe, r->pipe[0], E_READ, reactor_waked_up, NULL); 206 | reactor_add_event(r, &r->pe); 207 | 208 | if(handle_sig){ 209 | //set up signal events handling 210 | if(reacotr_signal_init(r) == -1){ 211 | LOG("failed to initialize signal handling."); 212 | reactor_destroy(r); 213 | } 214 | } 215 | if(handle_timer){ 216 | //set up timer events handling 217 | if(reactor_timer_init(r) == -1){ 218 | LOG("failed to initialize signal handling."); 219 | reactor_destroy(r); 220 | } 221 | } 222 | } 223 | 224 | void reactor_init(struct reactor * r, const char * policy_name){ 225 | reactor_init_(r, policy_name, 0, 0, 0); 226 | } 227 | void reactor_init_with_mt(struct reactor * r, const char * policy_name){ 228 | reactor_init_(r, policy_name, 1, 0, 0); 229 | } 230 | void reactor_init_with_signal(struct reactor * r, const char * policy_name){ 231 | reactor_init_(r, policy_name, 0, 1, 0); 232 | } 233 | 234 | void reactor_init_with_timer(struct reactor * r, const char * policy_name){ 235 | reactor_init_(r, policy_name, 0, 0, 1); 236 | } 237 | 238 | void reactor_init_with_signal_timer(struct reactor * r, const char * policy_name){ 239 | reactor_init_(r, policy_name, 0, 1, 1); 240 | } 241 | 242 | void reactor_init_with_mt_signal(struct reactor * r, const char * policy_name){ 243 | reactor_init_(r, policy_name, 1, 1, 0); 244 | } 245 | 246 | void reactor_init_with_mt_timer(struct reactor * r, const char * policy_name){ 247 | reactor_init_(r, policy_name, 1, 0, 1); 248 | } 249 | 250 | void reactor_init_with_mt_signal_timer(struct reactor * r, const char * policy_name){ 251 | reactor_init_(r, policy_name, 1, 1, 1); 252 | } 253 | 254 | /* 255 | * Frees up event_list 256 | * @r: the reactor 257 | */ 258 | static void reactor_free_events(struct reactor * r){ 259 | struct event * e = NULL; 260 | 261 | while((e = reactor_dequeue_event(r))){ 262 | if(r->policy->del(r, e->fd, e->ev_flags) == -1){ 263 | LOG("failed to remove the event[%d] from the reactor.", e->fd); 264 | } 265 | list_del(&e->event_link); 266 | 267 | event_ht_delete(&r->eht, e); 268 | 269 | list_del(&e->pending_link); 270 | 271 | e->rc = NULL; 272 | } 273 | } 274 | 275 | /* 276 | * Frees up event hash table 277 | * @r: the reactor 278 | */ 279 | static void reactor_free_hash(struct reactor * r){ 280 | event_ht_free(&r->eht); 281 | } 282 | 283 | void reactor_clean_events(struct reactor * r) { 284 | el_lock_lock(r->lock); 285 | reactor_free_events(r); 286 | if (r->pti) { 287 | timerheap_clean_events(r); 288 | } 289 | el_lock_unlock(r->lock); 290 | /* remove all events except this informing pipe */ 291 | event_set(&r->pe, r->pipe[0], E_READ, reactor_waked_up, NULL); 292 | if (reactor_add_event(r, &r->pe) == -1) { 293 | LOG("failed to add informing pipe event back to reactor"); 294 | } 295 | } 296 | 297 | void reactor_destroy(struct reactor * r){ 298 | el_lock_lock(r->lock); 299 | LOG("free up event_list."); 300 | //frees up event_list 301 | reactor_free_events(r); 302 | 303 | LOG("free up hash table."); 304 | //frees up hash table 305 | reactor_free_hash(r); 306 | 307 | LOG("free up polling policy."); 308 | //free up polling policy 309 | r->policy->destroy(r); 310 | free(r->policy); 311 | r->policy = r->policy_data = NULL; 312 | 313 | //close the pipe 314 | el_close_fd(r->pipe[0]); 315 | el_close_fd(r->pipe[1]); 316 | 317 | //close the signal event handling stuff 318 | if(r->psi){ 319 | el_close_fd(r->sig_pipe[0]); 320 | el_close_fd(r->sig_pipe[1]); 321 | signal_internal_restore_all(r); 322 | free(r->psi); 323 | free(r->sig_pe); 324 | r->psi = NULL; 325 | r->sig_pe = NULL; 326 | } 327 | 328 | //close the timer event handling stuff 329 | if(r->pti){ 330 | el_close_fd(r->sig_pipe[0]); 331 | el_close_fd(r->sig_pipe[1]); 332 | timerheap_destroy(r); 333 | free(r->pti); 334 | r->pti = NULL; 335 | } 336 | el_lock_unlock(r->lock); 337 | //free up the lock 338 | el_lock_destroy(r->lock); 339 | free(r->lock); 340 | } 341 | 342 | static int reacotr_signal_init(struct reactor * r){ 343 | if((r->psi = signal_internal_init(r)) == NULL){ 344 | LOG_EXIT(1, "failed on signal_internal_init"); 345 | return (-1); 346 | } 347 | 348 | if(el_create_pipe(r->sig_pipe) == -1){ 349 | LOG_EXIT(1, "failed to create signal informing pipe."); 350 | } 351 | 352 | /* set the pipe to nonblocking mode */ 353 | if(el_set_nonblocking(r->sig_pipe[0]) < 0 || el_set_nonblocking(r->sig_pipe[1]) < 0){ 354 | LOG_EXIT(1, "failed to set the pipe to nonblocking mode."); 355 | return (-1); 356 | } 357 | 358 | if((r->sig_pe = event_new(r->sig_pipe[0], E_READ, reactor_signal_callback, r)) == NULL){ 359 | LOG_EXIT(1, "failed to create event for signal events handling"); 360 | return (-1); 361 | } 362 | 363 | reactor_add_event(r, r->sig_pe); 364 | 365 | return (0); 366 | } 367 | 368 | /* 369 | * Initialize timer events handling mechanism. 370 | * Return: 0 on success, -1 on failure. 371 | * @r: the related reactor. 372 | */ 373 | int reactor_timer_init(struct reactor * r){ 374 | if((r->pti = timerheap_internal_init(r)) == NULL){ 375 | LOG("failed on timerheap_internal_init: %s", strerror(errno)); 376 | return (-1); 377 | } 378 | return (0); 379 | } 380 | 381 | int reactor_add_event(struct reactor * r, struct event * e){ 382 | assert(r != NULL && e != NULL); 383 | 384 | el_lock_lock(r->lock); 385 | 386 | if(e->ev_flags & E_SIGNAL || e->ev_flags & E_TIMEOUT){ 387 | /* Signal or timer event registration */ 388 | if(e->ev_flags & E_SIGNAL && e->ev_flags & E_TIMEOUT){ 389 | el_lock_unlock(r->lock); 390 | LOG("Inlivad flags[%d], E_SIGNAL and E_TIMEOUT are mutually exclusive.", e->ev_flags); 391 | return (-1); 392 | }else if(e->ev_flags & E_SIGNAL){ 393 | if(signal_internal_register(r, (int)e->fd, e) == -1){ 394 | el_lock_unlock(r->lock); 395 | LOG("failed to register event for signal[%d]", (int)e->fd); 396 | return (-1); 397 | } 398 | }else{//E_TIMEOUT 399 | int size = r->pti->size; 400 | assert(e->timerheap_idx == E_OUT_OF_TIMERHEAP); 401 | if(timerheap_add_event(r, e) == -1){ 402 | el_lock_unlock(r->lock); 403 | LOG("failed to register timer event"); 404 | return (-1); 405 | } 406 | assert(r->pti->size == size + 1); 407 | assert(e->timerheap_idx != E_OUT_OF_TIMERHEAP); 408 | } 409 | }else{ 410 | /* Normal I/O event registration. */ 411 | if(e->event_link.prev || e->event_link.next){ 412 | el_lock_unlock(r->lock); 413 | /* 414 | * This event is already in the reactor. 415 | * Assume every event only can be in one reactor. 416 | */ 417 | LOG("event already in the reactor"); 418 | return (-1); 419 | } 420 | //LOG("Adding a event [fd %d]", e->fd); 421 | if(r->policy->add(r, e->fd, e->ev_flags) == -1){ 422 | el_lock_unlock(r->lock); 423 | LOG("failed to add the event[%d] to the reactor.", e->fd); 424 | return (-1); 425 | } 426 | //LOG("Added a event [fd %d]", e->fd); 427 | list_add_tail(&e->event_link, &r->event_list); 428 | 429 | event_ht_insert(&r->eht, e, e->fd); 430 | } 431 | 432 | e->rc = r; 433 | e->ev_flags |= E_IN_REACTOR; 434 | 435 | //The polling thread might be sleeping indefinitely, wake it up. 436 | reactor_wake_up(r); 437 | 438 | el_lock_unlock(r->lock); 439 | 440 | return (0); 441 | } 442 | 443 | int reactor_modify_events(struct reactor * r, struct event * e){ 444 | assert(r != NULL && e != NULL); 445 | 446 | el_lock_lock(r->lock); 447 | 448 | if(e->ev_flags & E_SIGNAL || e->ev_flags & E_TIMEOUT){ 449 | LOG("Modification of signal or timer event is not supported"); 450 | return (-1); 451 | }else{ 452 | //LOG("Modifying the event [fd %d]", e->fd); 453 | if(r->policy->mod(r, e->fd, e->ev_flags) == -1){ 454 | el_lock_unlock(r->lock); 455 | LOG("failed to modify the event[%d] in the reactor.", e->fd); 456 | return (-1); 457 | } 458 | //LOG("Modifying the event [fd %d]", e->fd); 459 | } 460 | 461 | e->rc = r; 462 | e->ev_flags |= E_IN_REACTOR; 463 | 464 | //The polling thread might be sleeping indefinitely, wake it up. 465 | reactor_wake_up(r); 466 | 467 | el_lock_unlock(r->lock); 468 | 469 | return (0); 470 | } 471 | 472 | int reactor_add_to_pending(struct reactor * r, struct event * e, short res_flags){ 473 | assert(r != NULL && e != NULL); 474 | 475 | e->res_flags = res_flags; 476 | 477 | if(e->pending_link.prev || e->pending_link.next){ 478 | /* 479 | * This event is alrady in the pending list. 480 | * Assume every event only can be in one reactor. 481 | */ 482 | return (-1); 483 | } 484 | 485 | list_add_tail(&e->pending_link, &r->pending_list); 486 | 487 | return (0); 488 | } 489 | 490 | int reactor_remove_event(struct reactor * r, struct event * e){ 491 | assert(r != NULL && e != NULL); 492 | 493 | el_lock_lock(r->lock); 494 | if(e->ev_flags & E_SIGNAL || e->ev_flags & E_TIMEOUT){ 495 | /* Signal or timer event unregistration */ 496 | if(e->ev_flags & E_SIGNAL && e->ev_flags & E_TIMEOUT){ 497 | el_lock_unlock(r->lock); 498 | LOG("Inlivad flags[%d], E_SIGNAL and E_TIMEOUT are mutually exclusive.", e->ev_flags); 499 | return (-1); 500 | }else if(e->ev_flags & E_SIGNAL){ 501 | if(signal_internal_unregister(r, (int)e->fd) == -1){ 502 | el_lock_unlock(r->lock); 503 | LOG("failed to unregister event for signal[%d]", (int)e->fd); 504 | return (-1); 505 | } 506 | }else{//E_TIMEOUT 507 | if(e->timerheap_idx != E_OUT_OF_TIMERHEAP && timerheap_remove_event(r, e) == -1){ 508 | el_lock_unlock(r->lock); 509 | LOG("failed to unregister time event"); 510 | return (-1); 511 | } 512 | assert(e->timerheap_idx == E_OUT_OF_TIMERHEAP); 513 | } 514 | }else{ 515 | /* Normal I/O event unregistration. */ 516 | if(e->event_link.prev == NULL || e->event_link.next == NULL){ 517 | el_lock_unlock(r->lock); 518 | LOG("The event is not in the reactor."); 519 | /* 520 | * This event is not in the reactor. 521 | * Assume every event only can be in one reactor. 522 | */ 523 | return (-1); 524 | } 525 | //LOG("Removing a event [fd %d]", e->fd); 526 | if(r->policy->del(r, e->fd, e->ev_flags) == -1){ 527 | el_lock_unlock(r->lock); 528 | 529 | LOG("failed to remove the event[%d] from the reactor.", e->fd); 530 | return (-1); 531 | } 532 | //LOG("Removed a event [fd %d]", e->fd); 533 | 534 | list_del(&e->event_link); 535 | 536 | event_ht_delete(&r->eht, e); 537 | 538 | list_del(&e->pending_link); 539 | } 540 | 541 | e->rc = NULL; 542 | e->ev_flags &= ~E_IN_REACTOR; 543 | 544 | //The polling thread might be sleeping indefinitely, wake it up. 545 | reactor_wake_up(r); 546 | 547 | el_lock_unlock(r->lock); 548 | 549 | return (0); 550 | } 551 | 552 | /* 553 | * Dequeue a event from pending list. 554 | * Return: the dequeued event or null if the list is empty. 555 | * @r: the reactor. 556 | */ 557 | static struct event * reactor_dequeue_pending(struct reactor * r){ 558 | struct list_head * node; 559 | struct event * e; 560 | 561 | assert(r); 562 | 563 | if(list_empty(&r->pending_list)) 564 | return NULL; 565 | 566 | node = r->pending_list.next; 567 | e = list_entry(node, struct event, pending_link); 568 | 569 | list_del(&e->pending_link); 570 | return e; 571 | } 572 | 573 | static struct event * reactor_dequeue_event(struct reactor * r){ 574 | struct list_head * node; 575 | struct event * e; 576 | 577 | assert(r != NULL); 578 | 579 | if(list_empty(&r->event_list))return NULL; 580 | 581 | node = r->event_list.next; 582 | e = list_entry(node, struct event, event_link); 583 | 584 | list_del(&e->event_link); 585 | return e; 586 | } 587 | 588 | int reactor_event_empty(struct reactor * r){ 589 | assert(r != NULL); 590 | 591 | return list_empty(&r->event_list); 592 | } 593 | 594 | void reactor_loop(struct reactor * r, struct timeval * timeout, int flags){ 595 | int nreadys; 596 | struct timeval *pt, t; 597 | struct event * e; 598 | 599 | assert(r != NULL); 600 | 601 | while(!r->out){ 602 | el_lock_lock(r->lock); 603 | //LOG("start polling with timeout [%d, %d]", timeout ? timeout->tv_sec : -1, timeout ? timeout->tv_usec : -1); 604 | 605 | /* 606 | * On linux, the select syscall modifies timeout 607 | * to reflect the amount of time not slept. 608 | * We have to reset the timeout in order to be portable. 609 | */ 610 | if(timeout == NULL){ 611 | pt = NULL; 612 | }else{ 613 | t = *timeout; 614 | pt = &t; 615 | } 616 | 617 | if(r->pti){ 618 | /* 619 | * The timer event handling is supported, 620 | * have @pt point to the smallest timeval. 621 | */ 622 | struct timeval t; 623 | struct timeval * timerv = timerheap_top_timeout(r, &t); 624 | if(timerv && (pt == NULL || (pt && timer_s(*timerv, *pt)))){ 625 | t = *timerv; 626 | pt = &t; 627 | } 628 | } 629 | nreadys = r->policy->poll(r, pt); 630 | //LOG("stopped polling, got %d readys", nreadys); 631 | if(r->pti){ 632 | /* handle timer events */ 633 | reactor_handle_timer(r); 634 | } 635 | if(nreadys){ 636 | //iterate through pending events and call corresponding callbacks 637 | while(!r->out && (e = reactor_dequeue_pending(r))){ 638 | if(e->callback && event_in_reactor(e)){ 639 | el_lock_unlock(r->lock); 640 | 641 | e->callback(e->fd, e->res_flags, e->callback_arg); 642 | 643 | el_lock_lock(r->lock); 644 | } 645 | } 646 | if(flags & REACTOR_ONCE){ 647 | r->out = 1; 648 | } 649 | } 650 | el_lock_unlock(r->lock); 651 | } 652 | //LOG("told to stop the loop, stopped."); 653 | //reset the flag for next loop 654 | r->out = 0; 655 | } -------------------------------------------------------------------------------- /src/signal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | /* signal polling policy */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "cheetah/utility.h" 31 | #include "cheetah/polling_policy.h" 32 | #include "cheetah/event.h" 33 | #include "cheetah/reactor.h" 34 | #include "cheetah/signal.h" 35 | 36 | #ifdef WIN32 37 | #include 38 | #include 39 | #define __cdecl 40 | #else 41 | #include 42 | #endif 43 | 44 | /* 45 | * The reactor that is listening for signal events. 46 | */ 47 | static struct reactor * current_reactor; 48 | 49 | void sig_handler(int sig){ 50 | assert(current_reactor != NULL); 51 | if(current_reactor == NULL){ 52 | LOG("current_reactor is null!!"); 53 | return; 54 | } 55 | 56 | 57 | while(write(current_reactor->sig_pipe[1], &sig, 1) != 1); 58 | } 59 | 60 | 61 | struct signal_internal * signal_internal_init(struct reactor * r){ 62 | struct signal_internal *ret ; 63 | assert(r != NULL); 64 | 65 | if(current_reactor && current_reactor != r){ 66 | LOG("Only one reactor can handle signal events."); 67 | return NULL; 68 | } 69 | 70 | if((ret = malloc(sizeof(struct signal_internal))) == NULL){ 71 | LOG("failed to malloc for signal_internal."); 72 | return NULL; 73 | } 74 | 75 | memset(ret, 0, sizeof(struct signal_internal)); 76 | 77 | current_reactor = r; 78 | return ret; 79 | } 80 | 81 | 82 | int signal_internal_register(struct reactor * r, int sig, struct event * e){ 83 | struct signal_internal * psi; 84 | 85 | assert(r != NULL && e != NULL); 86 | if(current_reactor == NULL){ 87 | LOG("The current_reactor hasn't been set."); 88 | return (-1); 89 | }else if(current_reactor != r){ 90 | LOG("Only one reactor can handle signal events."); 91 | return (-1); 92 | }else if(r->psi == NULL){ 93 | LOG("The signal_internal hasn's been set."); 94 | return (-1); 95 | }else if(e == NULL){ 96 | LOG("event can not be null"); 97 | return (-1); 98 | }else if(sig < 1 || sig >= SIGNALN){ 99 | LOG("signal num[%d] is out of range[1-64]."); 100 | return (-1); 101 | } 102 | 103 | psi = r->psi; 104 | 105 | struct sigaction setup_action; 106 | sigset_t block_mask; 107 | 108 | /* block all other signal except for the one being registered */ 109 | sigfillset(&block_mask); 110 | sigdelset(&block_mask, sig); 111 | setup_action.sa_handler = sig_handler; 112 | setup_action.sa_mask = block_mask; 113 | setup_action.sa_flags = 0; 114 | 115 | if(sigaction(sig, &setup_action, &psi->old_actions[sig]) == -1){ 116 | LOG("sigaction failed: %s", strerror(errno)); 117 | return (-1); 118 | } 119 | 120 | psi->sigevents[sig] = e; 121 | 122 | return (0); 123 | } 124 | 125 | int signal_internal_restore_all(struct reactor * r){ 126 | struct signal_internal * psi; 127 | int i; 128 | 129 | assert(r != NULL); 130 | if(current_reactor == NULL){ 131 | LOG("The current_reactor hasn't been set."); 132 | return (-1); 133 | }else if(current_reactor != r){ 134 | LOG("Only one reactor can handle signal events."); 135 | return (-1); 136 | }else if(r->psi == NULL){ 137 | LOG("The signal_internal hasn's been set."); 138 | return (-1); 139 | } 140 | 141 | psi = r->psi; 142 | 143 | for(i = 1; i < SIGNALN; ++i){ 144 | if(psi->sigevents[i] && sigaction(i, &psi->old_actions[i], NULL) == -1){ 145 | LOG("sigaction failed: %s", strerror(errno)); 146 | } 147 | psi->sigevents[i] = NULL; 148 | } 149 | 150 | return (0); 151 | } 152 | 153 | int signal_internal_unregister(struct reactor * r, int sig){ 154 | struct signal_internal * psi; 155 | 156 | assert(r != NULL); 157 | if(current_reactor == NULL){ 158 | LOG("The current_reactor hasn't been set."); 159 | return (-1); 160 | }else if(current_reactor != r){ 161 | LOG("Only one reactor can handle signal events."); 162 | return (-1); 163 | }else if(r->psi == NULL){ 164 | LOG("The signal_internal hasn's been set."); 165 | return (-1); 166 | }else if(sig < 1 || sig >= SIGNALN){ 167 | LOG("signal num[%d] is out of range(1-64)."); 168 | return (-1); 169 | } 170 | psi = r->psi; 171 | 172 | if (psi->sigevents[sig] == NULL) { 173 | LOG("signal num[%d] is not registered on this reactor."); 174 | return (-1); 175 | } 176 | 177 | if(sigaction(sig, &psi->old_actions[sig], NULL) == -1){ 178 | LOG("sigaction failed: %s", strerror(errno)); 179 | return (-1); 180 | } 181 | 182 | psi->sigevents[sig] = NULL; 183 | 184 | return (0); 185 | } 186 | -------------------------------------------------------------------------------- /src/test_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "cheetah/reactor.h" 12 | 13 | void stdin_read(el_socket_t fd, short res_flags, void * arg){ 14 | printf("&struct event: %p, ", arg); 15 | if(res_flags & E_READ) 16 | printf("stdin read event "); 17 | if(res_flags & E_WRITE) 18 | printf("stdin read event."); 19 | printf("\n"); 20 | } 21 | void stdout_write(el_socket_t fd, short res_flags, void * arg){ 22 | printf("&struct event: %p, ", arg); 23 | if(res_flags & E_READ) 24 | printf("stdout read event "); 25 | if(res_flags & E_WRITE) 26 | printf("stdout read event."); 27 | printf("\n"); 28 | } 29 | void stderr_read_write(el_socket_t fd, short res_flags, void * arg){ 30 | printf("&struct event: %p, ", arg); 31 | if(res_flags & E_READ) 32 | printf("stderr read event "); 33 | if(res_flags & E_WRITE) 34 | printf("stderr write event. "); 35 | printf("\n"); 36 | } 37 | struct reactor r; 38 | struct event e1, e2; 39 | 40 | void * thread(void * arg){ 41 | pthread_detach(pthread_self()); 42 | sleep(10); 43 | LOG("about to stop the loop."); 44 | reactor_get_out(&r); 45 | } 46 | 47 | void read_file(el_socket_t fd, short res_flags, void * arg){ 48 | struct event * e = (struct event * )arg; 49 | char buf[1024]; 50 | int n; 51 | n = read(fd, buf, sizeof(buf) - 1); 52 | buf[n] = 0; 53 | printf("%s", buf); 54 | printf("\n"); 55 | if(n == 0){ 56 | close(fd); 57 | reactor_remove_event(&r, e); 58 | free(e); 59 | } 60 | } 61 | int echoing_counts = 0; 62 | void echo_callback(el_socket_t fd, short res_flags, void * arg){ 63 | struct event * e = (struct event *) arg; 64 | char buf[4096]; 65 | int n; 66 | n = read(fd, buf, sizeof(buf) - 1); 67 | buf[n] = 0; 68 | printf("got: %s\n", buf); 69 | write(fd, buf, n); 70 | if(n == 0){//peer has closed the connection 71 | reactor_remove_event(&r, e); 72 | close(fd); 73 | } 74 | if(++echoing_counts == 1000){ 75 | reactor_get_out(&r); 76 | } 77 | } 78 | 79 | void accept_callback(el_socket_t fd, short res_flags, void * arg){ 80 | el_socket_t csock = accept(fd, NULL, NULL); 81 | reactor_add_event(&r, event_new(csock, E_READ, echo_callback, NULL)); 82 | } 83 | 84 | void * main_thread(void * arg){ 85 | pthread_detach(pthread_self()); 86 | reactor_loop(&r, NULL); 87 | } 88 | /*int main(int argc, char const *argv[]){ 89 | el_socket_t listen_sock; 90 | struct sockaddr_in saddr; 91 | unsigned short port; 92 | int optval = 1; 93 | port = htons(8888); 94 | 95 | memset(&saddr, 0, sizeof(saddr)); 96 | saddr.sin_family = AF_INET; 97 | saddr.sin_addr.s_addr = htonl(INADDR_ANY); 98 | saddr.sin_port = port; 99 | listen_sock = socket(AF_INET, SOCK_STREAM, 0); 100 | if(listen_sock == -1){ 101 | LOG_EXIT(1, "failed on socket: %s", strerror(errno)); 102 | } 103 | if(bind(listen_sock, (struct sockaddr *)&saddr, sizeof(saddr))){ 104 | LOG_EXIT(1, "failed on bind: %s", strerror(errno)); 105 | } 106 | if(setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))){ 107 | LOG_EXIT(1, "failed on setsockopt: %s", strerror(errno)); 108 | } 109 | if(listen(listen_sock, 10)){ 110 | LOG_EXIT(1, "failed on listen: %s", strerror(errno)); 111 | } 112 | 113 | reactor_init(&r, "epoll", 0); 114 | reactor_add_event(&r, event_new(listen_sock, E_READ, accept_callback, NULL)); 115 | 116 | reactor_loop(&r, NULL); 117 | reactor_destroy(&r); 118 | 119 | return 0; 120 | } 121 | */ -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "cheetah/log.h" 29 | #include "cheetah/timer.h" 30 | #include "cheetah/reactor.h" 31 | #include "cheetah/event.h" 32 | #include "cheetah/utility.h" 33 | 34 | /* 35 | * Heapify the timerheap in a top-down way after 36 | * alteration of the heap with the @idxth entry being the top entry. 37 | * This function is used when deleting a entry from the heap. 38 | * @pti: The related timerheap_internal structure. 39 | * @idx: The top entry's index. 40 | */ 41 | static void timerheap_heapify_topdown(struct timerheap_internal * pti, int idx); 42 | 43 | struct timerheap_internal * timerheap_internal_init(){ 44 | struct timerheap_internal * ret; 45 | struct heap_entry * heap; 46 | if((ret = malloc(sizeof(struct timerheap_internal))) == NULL){ 47 | LOG("failed to malloc for timerheap_internal: %s", strerror(errno)); 48 | return NULL; 49 | } 50 | memset(ret, 0, sizeof(struct timerheap_internal)); 51 | 52 | if((heap = malloc(TIMERHEAP_INIT_SIZE * sizeof(struct heap_entry))) == NULL){ 53 | LOG("failed to malloc for heap_entry: %s", strerror(errno)); 54 | free(ret); 55 | return NULL; 56 | } 57 | ret->heap = heap; 58 | ret->size = 0; 59 | ret->capacity = TIMERHEAP_INIT_SIZE; 60 | return ret; 61 | } 62 | 63 | /* 64 | * Expand the timerheap to be big enough to hold @size entries. 65 | * Return: 0 on success, -1 on failure. 66 | * @pti: the timerheap_internal to expand. 67 | */ 68 | static int timerheap_grow(struct timerheap_internal * pti, int size){ 69 | int new_cap; 70 | struct heap_entry * new_heap; 71 | assert(pti != NULL); 72 | assert(size > 1); 73 | 74 | new_cap = pti->capacity; 75 | 76 | while(new_cap <= size){ 77 | new_cap <<= 1; 78 | } 79 | 80 | if((new_heap = realloc(pti->heap, sizeof(struct heap_entry) * new_cap)) == NULL){ 81 | LOG("failed on realloc: %s", strerror(errno)); 82 | return (-1); 83 | } 84 | 85 | pti->heap = new_heap; 86 | pti->capacity = new_cap; 87 | LOG("expanded to %d.", pti->capacity); 88 | return (0); 89 | } 90 | /* 91 | * Initialize the heap entry with timer event. 92 | * Return: a newly created heap_entry on success, NULL on failure. 93 | * @e: the event to be bound to the the entry. 94 | */ 95 | static void init_heap_entry(struct heap_entry * phe, struct event * e){ 96 | assert(phe != NULL); 97 | assert(e != NULL); 98 | 99 | el_gettimeofday(&phe->expiration); 100 | timer_add(phe->expiration, e->fd); 101 | phe->e = e; 102 | } 103 | 104 | /* 105 | * Tests whether the heap is empty. 106 | * Return: 0 if the heap has entries, 1 if the heap is empty. 107 | * @pti: The related timerheap_internal structure. 108 | */ 109 | static inline int timerheap_empty(struct timerheap_internal * pti){ 110 | assert(pti != NULL); 111 | return pti->size == 0; 112 | } 113 | 114 | int timerheap_top_expired(struct reactor * r){ 115 | struct timeval cur; 116 | 117 | assert(r != NULL); 118 | assert(r->pti != NULL); 119 | if(timerheap_empty(r->pti)){ 120 | return (0); 121 | } 122 | 123 | el_gettimeofday(&cur); 124 | /* 125 | LOG("cur: %ld, exp: %ld, diff: %ld", timer_to_ms(cur), 126 | timer_to_ms(r->pti->heap[1].expiration), 127 | timer_to_ms(r->pti->heap[1].expiration) - timer_to_ms(cur)); 128 | */ 129 | return timer_se(r->pti->heap[1].expiration, cur); 130 | } 131 | 132 | struct timeval * timerheap_top_timeout(struct reactor * r, struct timeval * timeout){ 133 | struct timeval cur; 134 | 135 | assert(r != NULL); 136 | assert(r->pti != NULL); 137 | if(timerheap_empty(r->pti)){ 138 | return NULL; 139 | } 140 | *timeout = r->pti->heap[1].expiration; 141 | el_gettimeofday(&cur); 142 | el_timesub(timeout, &cur, timeout); 143 | if(timer_to_ms(*timeout) < 0){ 144 | timeout->tv_sec = timeout->tv_usec = 0; 145 | } 146 | return timeout; 147 | } 148 | 149 | struct event * timerheap_get_top(struct reactor * r){ 150 | assert(r != NULL); 151 | assert(r->pti != NULL); 152 | 153 | if(timerheap_empty(r->pti)){ 154 | return NULL; 155 | } 156 | 157 | return r->pti->heap[1].e; 158 | } 159 | 160 | struct event * timerheap_pop_top(struct reactor * r){ 161 | struct timerheap_internal * pti; 162 | struct event * pe; 163 | assert(r != NULL); 164 | assert(r->pti != NULL); 165 | 166 | pti = r->pti; 167 | 168 | if(timerheap_empty(pti)){ 169 | return NULL; 170 | } 171 | 172 | pe = pti->heap[1].e; 173 | 174 | if(timerheap_remove_event(r, pe) == -1){ 175 | LOG("failed to remove timer."); 176 | return NULL; 177 | } 178 | return pe; 179 | } 180 | 181 | /* 182 | * Heapify the timerheap in a bottom-up way after 183 | * alteration of the heap with the @idxth entry being the bottom entry. 184 | * This function is used when inserting a entry to the heap. 185 | * @pti: The related timerheap_internal structure. 186 | * @idx: The bottom entry's index. 187 | */ 188 | static void timerheap_heapify_bottomup(struct timerheap_internal * pti, int idx){ 189 | int parent; 190 | struct heap_entry * heap; 191 | struct heap_entry he; 192 | 193 | assert(pti != NULL); 194 | assert(idx > 0 && idx <= pti->size); 195 | 196 | heap = pti->heap; 197 | he = heap[idx]; 198 | 199 | parent = idx >> 1; 200 | 201 | while(parent && timer_ge(heap[parent].expiration, he.expiration)){ 202 | heap[idx] = heap[parent]; 203 | heap[idx].e->timerheap_idx = idx; 204 | idx = parent; 205 | parent >>= 1; 206 | } 207 | 208 | heap[idx] = he; 209 | heap[idx].e->timerheap_idx = idx; 210 | } 211 | 212 | inline void timerheap_reset_timer(struct reactor * r, struct event * e){ 213 | assert(r != NULL); 214 | assert(e != NULL); 215 | 216 | init_heap_entry(&r->pti->heap[e->timerheap_idx], e); 217 | timerheap_heapify_topdown(r->pti, e->timerheap_idx); 218 | } 219 | 220 | static void timerheap_heapify_topdown(struct timerheap_internal * pti, int idx){ 221 | int child, i, size; 222 | struct heap_entry * heap; 223 | struct heap_entry he; 224 | 225 | assert(pti != NULL); 226 | assert(idx > 0 && idx <= pti->size); 227 | 228 | size = pti->size; 229 | heap = pti->heap; 230 | he = heap[idx]; 231 | 232 | for(i = idx; i<= (size >> 1); i = child){ 233 | child = i << 1; 234 | 235 | if(child + 1 <= size && 236 | timer_g(heap[child].expiration, heap[child + 1].expiration)) 237 | ++child; 238 | 239 | if(timer_g(heap[child].expiration, he.expiration)) 240 | break; 241 | 242 | heap[i] = heap[child]; 243 | heap[i].e->timerheap_idx = i; 244 | } 245 | 246 | heap[i] = he; 247 | heap[i].e->timerheap_idx = i; 248 | } 249 | 250 | int timerheap_add_event(struct reactor * r, struct event * e){ 251 | struct timerheap_internal * pti; 252 | assert(r != NULL); 253 | assert(r->pti != NULL); 254 | assert(e != NULL); 255 | 256 | pti = r->pti; 257 | 258 | if(e->timerheap_idx != E_OUT_OF_TIMERHEAP){ 259 | LOG("The timer event is already in the heap."); 260 | return (-1); 261 | } 262 | 263 | if(pti->size + 1 >= pti->capacity && timerheap_grow(pti, pti->size + 1) == -1){ 264 | LOG("failed on timerheap_grow: %s", strerror(errno)); 265 | return (-1); 266 | } 267 | 268 | /* 269 | * Initialize the heap_entry and heapfiy the heap. 270 | */ 271 | init_heap_entry(&pti->heap[++pti->size], e); 272 | timerheap_heapify_bottomup(pti, pti->size); 273 | 274 | return (0); 275 | } 276 | 277 | int timerheap_remove_event(struct reactor * r, struct event * e){ 278 | struct timerheap_internal * pti; 279 | struct heap_entry *heap; 280 | assert(r != NULL); 281 | assert(r->pti != NULL); 282 | assert(e != NULL); 283 | 284 | if(e->timerheap_idx == E_OUT_OF_TIMERHEAP){ 285 | LOG("The timer event is not in the heap."); 286 | return (-1); 287 | } 288 | pti = r->pti; 289 | heap = pti->heap; 290 | /* 291 | * Fills in the entry being removed with the 292 | * rearest entry and heapify the heap. 293 | */ 294 | heap[e->timerheap_idx] = heap[pti->size--]; 295 | 296 | /* 297 | * We might encounter that the @e is the rearest 298 | * entry in the heap. In that case, we simply 299 | * skip the heapification. 300 | */ 301 | if(e->timerheap_idx <= pti->size) 302 | timerheap_heapify_topdown(pti, e->timerheap_idx); 303 | e->timerheap_idx = E_OUT_OF_TIMERHEAP; 304 | return (0); 305 | } 306 | 307 | /* 308 | * Free up resources used by the timerhea. 309 | * Return: 0 on success, -1 on failure. 310 | * @pti: The related timerheap_internal structure. 311 | */ 312 | static int timerheap_free(struct timerheap_internal * pti){ 313 | assert(pti != NULL); 314 | 315 | free(pti->heap); 316 | pti->heap = NULL; 317 | pti->size = pti->capacity = 0; 318 | 319 | return (0); 320 | } 321 | 322 | int timerheap_clean_events(struct reactor *r){ 323 | while(timerheap_pop_top(r)); 324 | return (0); 325 | } 326 | 327 | int timerheap_destroy(struct reactor * r){ 328 | struct timerheap_internal * pti; 329 | assert(r != NULL); 330 | assert(r->pti != NULL); 331 | 332 | pti = r->pti; 333 | 334 | timerheap_free(pti); 335 | free(pti); 336 | r->pti = NULL; 337 | 338 | return (0); 339 | } -------------------------------------------------------------------------------- /src/utility.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include "cheetah/includes.h" 25 | #include "cheetah/utility.h" 26 | 27 | inline int el_create_pipe(el_socket_t p[2]){ 28 | #ifdef WIN32 29 | return CreatePipe(&p[0], &p[1], NULL, 0) ? 0 : -1; 30 | #else 31 | return pipe(p); 32 | #endif 33 | } 34 | inline int el_close_fd(el_socket_t fd){ 35 | #ifdef WIN32 36 | return closesocket(fd) ? -1 : 0; 37 | #else 38 | return close(fd); 39 | #endif 40 | } 41 | 42 | inline int el_gettimeofday(struct timeval * tv){ 43 | #ifdef WIN32 44 | union{ 45 | long long ns100; 46 | FILETIME ft; 47 | }u_time; 48 | GetSystemTimeAsFileTime (&u_time.ft); 49 | tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); 50 | tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); 51 | return (0); 52 | #else 53 | return gettimeofday(tv, NULL); 54 | #endif 55 | } 56 | 57 | inline int el_set_nonblocking(el_socket_t fd){ 58 | #ifdef WIN32 59 | ULONG nonBlock = 1; 60 | if (ioctlsocket(m_Socket, FIONBIO, &nonBlock)){ 61 | LOG("failed on ioctlsocket"); 62 | return (-1); 63 | } 64 | return (0); 65 | #else 66 | int opts; 67 | 68 | if((opts = fcntl(fd, F_GETFL)) < 0){ 69 | LOG("failed on fcntl before: %s", strerror(errno)); 70 | return (-1); 71 | } 72 | 73 | opts |= O_NONBLOCK; 74 | if(fcntl(fd, F_SETFL, opts) < 0){ 75 | LOG("failed on fcntl after: %s", strerror(errno)); 76 | return (-1); 77 | } 78 | 79 | return (0); 80 | #endif 81 | } -------------------------------------------------------------------------------- /test/benchmark.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "cheetah/reactor.h" 33 | 34 | struct reactor r; 35 | struct event * events; 36 | static int count, fired; 37 | static int num_pipes, num_active; 38 | static el_socket_t * pipes; 39 | void read_cb(el_socket_t fd, short res_flags, void * arg){ 40 | char buf[1024]; 41 | int n = read(fd, buf, sizeof(buf)); 42 | buf[n] = 0; 43 | //printf("got: %s\n", buf); 44 | ++count; 45 | } 46 | 47 | static struct timeval * run_once(void){ 48 | el_socket_t *cp, space; 49 | long i; 50 | static struct timeval ts, te; 51 | 52 | for(cp = pipes, i = 0; i < num_pipes; ++i, cp += 2){ 53 | if(event_in_reactor(&events[i])) 54 | reactor_remove_event(&r, &events[i]); 55 | event_set(&events[i], cp[0], E_READ, read_cb, NULL); 56 | reactor_add_event(&r, &events[i]); 57 | } 58 | 59 | fired = 0; 60 | space = num_pipes / num_active; 61 | space = space * 2; 62 | for (i = 0; i < num_active; i++, fired++){ 63 | //LOG("writing \"e\" to %d", pipes[i * space + 1]); 64 | write(pipes[i * space + 1], "e", 1); 65 | //perror("write"); 66 | } 67 | 68 | count = 0; 69 | 70 | //LOG("start looping"); 71 | { int xcount = 0; 72 | el_gettimeofday(&ts); 73 | do { 74 | reactor_loop(&r, NULL, REACTOR_ONCE); 75 | xcount++; 76 | } while (count != fired); 77 | el_gettimeofday(&te); 78 | 79 | if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count); 80 | } 81 | //LOG("done looping"); 82 | el_timesub(&te, &ts, &te); 83 | printf("time spent in event processing: %ldμs\n", te.tv_sec * 1000000 + te.tv_usec); 84 | return (&te); 85 | } 86 | 87 | int main(int argc, char const *argv[]){ 88 | struct rlimit rl; 89 | int i, c; 90 | struct timeval *tv; 91 | static struct timeval ts, te; 92 | el_socket_t *cp; 93 | 94 | while((c = getopt(argc, argv, "a:n:")) != -1){ 95 | switch(c){ 96 | case 'a': 97 | num_active = atoi(optarg); 98 | break; 99 | case 'n': 100 | num_pipes = atoi(optarg); 101 | break; 102 | default: 103 | fprintf(stderr, "Illegal argument \"%c\"\n", c); 104 | exit(1); 105 | break; 106 | } 107 | } 108 | 109 | rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; 110 | if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { 111 | perror("setrlimit"); 112 | exit(1); 113 | } 114 | 115 | reactor_init(&r, NULL); 116 | 117 | events = calloc(num_pipes, sizeof(struct event)); 118 | pipes = calloc(num_pipes << 1, sizeof(el_socket_t)); 119 | if (events == NULL || pipes == NULL) { 120 | perror("calloc"); 121 | exit(1); 122 | } 123 | 124 | for(cp = pipes, i = 0; i < num_pipes; ++i, cp += 2){ 125 | if(el_create_pipe(cp) == -1){ 126 | perror("pipe"); 127 | exit(1); 128 | } 129 | } 130 | 131 | for (i = 0; i < 25; i++) { 132 | el_gettimeofday(&ts); 133 | tv = run_once(); 134 | el_gettimeofday(&te); 135 | el_timesub(&te, &ts, &te); 136 | if (tv == NULL) 137 | exit(1); 138 | fprintf(stdout, "total time per iteration: %ldμs\n", 139 | tv->tv_sec * 1000000 + tv->tv_usec); 140 | } 141 | 142 | reactor_destroy(&r); 143 | free(events); 144 | free(pipes); 145 | return 0; 146 | } -------------------------------------------------------------------------------- /test/io_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | struct reactor r; 35 | struct event e; 36 | int p[2]; 37 | 38 | void read_cb(el_socket_t fd, short res_flags, void * arg){ 39 | char buf[1024]; 40 | int n = read(fd, buf, sizeof(buf)); 41 | buf[n] = 0; 42 | fprintf(stderr, "got: %s\n", buf); 43 | fprintf(stderr, "modify to listen to write event\n"); 44 | event_modify_events(&e, E_WRITE); 45 | if (reactor_modify_events(&r, &e) == -1) { 46 | exit(-1); 47 | } 48 | } 49 | 50 | void write_cb(el_socket_t fd, short res_flags, void * arg){ 51 | fprintf(stderr, "write event is fired\n"); 52 | reactor_get_out(&r); 53 | } 54 | 55 | int main(int argc, char const *argv[]){ 56 | pipe(p); 57 | reactor_init(&r, NULL); 58 | event_set(&e, p[0], E_READ, read_cb, NULL); 59 | if (reactor_add_event(&r, &e) == -1) { 60 | fprintf(stderr, "failed to add event to the reactor\n"); 61 | exit(-1); 62 | } 63 | write(p[1], "0", 1); 64 | reactor_loop(&r, NULL, 0); 65 | reactor_destroy(&r); 66 | return 0; 67 | } -------------------------------------------------------------------------------- /test/signal_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "cheetah/reactor.h" 33 | struct reactor r; 34 | struct event e; 35 | 36 | void sigint_callback(el_socket_t fd, short flags, void * arg){ 37 | static int count = 0; 38 | LOG("sigint_callback got called"); 39 | struct reactor * r = arg; 40 | reactor_get_out(r); 41 | } 42 | 43 | void * thread_func(void * arg){ 44 | pthread_detach(pthread_self()); 45 | sleep(10); 46 | LOG("Removing the signal event"); 47 | reactor_remove_event(&r, &e); 48 | 49 | return NULL; 50 | } 51 | 52 | int main(int argc, char const *argv[]){ 53 | reactor_init_with_signal(&r, NULL); 54 | 55 | event_set(&e, SIGINT, E_SIGNAL, sigint_callback, &r); 56 | reactor_add_event(&r, &e); 57 | 58 | pthread_t thread_id; 59 | pthread_create(&thread_id, NULL, thread_func, NULL); 60 | 61 | reactor_loop(&r, NULL, 0); 62 | 63 | reactor_destroy(&r); 64 | 65 | return 0; 66 | } -------------------------------------------------------------------------------- /test/timer_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Xinjing Chow 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 3. The name of the author may not be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERR 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "cheetah/reactor.h" 34 | struct reactor r; 35 | struct event e[14]; 36 | 37 | int count; 38 | void timer_callback(el_socket_t fd, short flags, void * arg){ 39 | time_t t; 40 | ++count; 41 | time(&t); 42 | //LOG("%s\n", ctime(&t)); 43 | } 44 | void * thread_func(void * arg){ 45 | pthread_detach(pthread_self()); 46 | struct event * pe = arg; 47 | sleep(10); 48 | LOG("thread[%u]:Removing the timer event", pthread_self()); 49 | reactor_remove_event(&r, pe); 50 | sleep(5); 51 | event_set(pe, 1000, E_TIMEOUT, timer_callback, pe); 52 | LOG("thread[%u]:adding the timer event", pthread_self()); 53 | reactor_add_event(&r, pe); 54 | sleep(10); 55 | reactor_remove_event(&r, pe); 56 | if(pe == &e[0]){ 57 | reactor_get_out(&r); 58 | } 59 | } 60 | int main(int argc, char const *argv[]){ 61 | pthread_t thread_id[14]; 62 | int i; 63 | reactor_init_with_mt_timer(&r, NULL); 64 | for(i = 0; i < 14; ++i){ 65 | event_set(&e[i], (i + 1) * 400, E_TIMEOUT, timer_callback, &e[i]); 66 | reactor_add_event(&r, &e[i]); 67 | pthread_create(&thread_id[i], NULL, thread_func, &e[i]); 68 | } 69 | reactor_loop(&r, NULL, 0); 70 | //sleep(10); 71 | reactor_destroy(&r); 72 | LOG("count:%d", count); 73 | return 0; 74 | } --------------------------------------------------------------------------------