-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathpoll.c
235 lines (225 loc) · 7.68 KB
/
poll.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include "poll.h"
#include "debug.h"
#include "hash_table/hashtable.h"
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include<unistd.h>
/**
* License GPLv3+
*/
//poll_event_element functions
/**
* Function to allocate a new poll event element
* @param fd the file descriptor to watch
* @param events epoll events mask
* @returns poll event element on success
* @returns NULL on failure
*/
poll_event_element_t * poll_event_element_new(int fd, uint32_t events)
{
INFO("Creating a new poll event element");
poll_event_element_t *elem = calloc(1, poll_event_element_s);
if (elem)
{
elem->fd = fd;
elem->events = events;
}
return elem;
}
/**
* Function to delete a poll event element
* @param elem poll event element
*/
void poll_event_element_delete(poll_event_element_t * elem)
{
INFO("Deleting a poll event element");
free(elem);
}
// poll_event function
/**
* Function to create a new poll event object
* @param timeout timeout for the pollevent
* @retunrs NULL on failure
* @retunrs poll event object on sucess
*/
poll_event_t * poll_event_new(int timeout)
{
poll_event_t * poll_event = calloc(1, poll_event_s);
if (!poll_event)
{
INFO("calloc failed at poll_event");
return NULL; // No Memory
}
poll_event->table = hash_table_new(MODE_VALUEREF);
if (!poll_event->table)
{
free(poll_event);
INFO("calloc failed at hashtble");
return NULL;
}
poll_event->timeout = timeout;
poll_event->epoll_fd = epoll_create(MAX_EVENTS);
INFO("Created a new poll event");
return poll_event;
}
/**
* Function to delete poll event object
* @param poll_event poll event object to be deleted
*/
void poll_event_delete(poll_event_t* poll_event)
{
INFO("deleting a poll_event");
hash_table_delete(poll_event->table);
close(poll_event->epoll_fd);
free(poll_event);
}
/**
* Function to add a file descriptor to the event poll obeject
* @note if add is performed on an fd already in poll_event, the flags are updated in the existing object
* @param poll_event poll event object which fd has to be added
* @param fd the file descriptor to be added
* @param flags events flags from epoll
* @param poll_element a poll event element pointer which is filled in by the function, set all function callbacks and cb_flags in this
*/
int poll_event_add(poll_event_t* poll_event, int fd, uint32_t flags, poll_event_element_t **poll_element)
{
poll_event_element_t *elem = NULL;
elem = (poll_event_element_t *) HT_LOOKUP(poll_event->table, &fd);
if (elem)
{
LOG("fd (%d) already added updating flags", fd);
elem->events |= flags;
struct epoll_event ev;
memset(&ev, 0, sizeof(struct epoll_event));
ev.data.fd = fd;
ev.events = elem->events;
*poll_element = elem;
return epoll_ctl(poll_event->epoll_fd, EPOLL_CTL_MOD, fd, &ev);
}
else
{
elem = poll_event_element_new(fd, flags);
if (HT_ADD(poll_event->table, &fd, elem))
{
// error in hash table
return -1;
}
LOG("Added fd(%d)", fd);
struct epoll_event ev;
memset(&ev, 0, sizeof(struct epoll_event));
ev.data.fd = fd;
ev.events = elem->events;
*poll_element = elem;
return epoll_ctl(poll_event->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
}
}
/**
* Function to remove a poll event element from the given poll_event object
* @param poll_event poll event object from which fd has to be removed
* @param fd file descriptor which has to be removed
*/
int poll_event_remove(poll_event_t* poll_event, int fd)
{
// TODO Handle error
HT_REMOVE(poll_event->table, &fd);
close(fd);
epoll_ctl(poll_event->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
return 0;
}
/**
* Function which processes the events from epoll_wait and calls the appropriate callbacks
* @note only process events once if you need to use an event loop use poll_event_loop
* @param poll_event poll event object to be processed
*/
int poll_event_process(poll_event_t * poll_event)
{
struct epoll_event events[MAX_EVENTS];
INFO("May the source be with you!!");
int fds = epoll_wait(poll_event->epoll_fd, events, MAX_EVENTS, poll_event->timeout);
if (fds == 0)
{
INFO("event loop timed out");
if (poll_event->timeout_callback)
{
if (poll_event->timeout_callback(poll_event))
{
return -1;
}
}
}
int i = 0;
for(;i<fds;i++)
{
poll_event_element_t * value = NULL;
if ((value = (poll_event_element_t *) HT_LOOKUP(poll_event->table, &events[i].data.fd)) != NULL)
{
LOG("started processing for event id(%d) and sock(%d)", i, events[i].data.fd);
// when data avaliable for read or urgent flag is set
if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLPRI))
{
if (events[i].events & EPOLLIN)
{
LOG("found EPOLLIN for event id(%d) and sock(%d)", i, events[i].data.fd);
value->cur_event &= EPOLLIN;
}
else
{
LOG("found EPOLLPRI for event id(%d) and sock(%d)", i, events[i].data.fd);
value->cur_event &= EPOLLPRI;
}
/// connect or accept callbacks also go through EPOLLIN
/// accept callback if flag set
if ((value->cb_flags & ACCEPT_CB) && (value->accept_callback))
value->accept_callback(poll_event, value, events[i]);
/// connect callback if flag set
if ((value->cb_flags & CONNECT_CB) && (value->connect_callback))
value->connect_callback(poll_event, value, events[i]);
/// read callback in any case
if (value->read_callback)
value->read_callback(poll_event, value, events[i]);
}
// when write possible
if (events[i].events & EPOLLOUT)
{
LOG("found EPOLLOUT for event id(%d) and sock(%d)", i, events[i].data.fd);
value->cur_event &= EPOLLOUT;
if (value->write_callback)
value->write_callback(poll_event, value, events[i]);
}
// shutdown or error
if ( (events[i].events & EPOLLRDHUP) || (events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP))
{
if (events[i].events & EPOLLRDHUP)
{
LOG("found EPOLLRDHUP for event id(%d) and sock(%d)", i, events[i].data.fd);
value->cur_event &= EPOLLRDHUP;
}
else
{
LOG("found EPOLLERR for event id(%d) and sock(%d)", i, events[i].data.fd);
value->cur_event &= EPOLLERR;
}
if (value->close_callback)
value->close_callback(poll_event, value, events[i]);
}
}
else // not in table
{
LOG("WARNING: NOT FOUND hash table value for event id(%d) and sock(%d)", i, events[i].data.fd);
}
} // for
return 0;
}
/**
* Function to start the event loop which monitors all fds and callbacks accordingly
* @note event loop runs indefinitely and can only be stopped by timeout callback, so to process events only once use poll_event_process
*/
void poll_event_loop(poll_event_t* poll_event)
{
INFO("Entering the main event loop for epoll lib");
while(!poll_event_process(poll_event));
}