├── .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 | }
--------------------------------------------------------------------------------