2
* @file geis_backend_multiplexor.c
3
* @brief internal GEIS backend multiplexor implementation
5
* Copyright 2010, 2012 Canonical Ltd.
7
* This library is free software; you can redistribute it and/or modify it under
8
* the terms of the GNU Lesser General Public License as published by the Free
9
* Software Foundation; either version 3 of the License, or (at your option) any
12
* This library is distributed in the hope that it will be useful, but WITHOUT
13
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17
* You should have received a copy of the GNU Lesser General Public License
18
* along with this program; if not, write to the Free Software Foundation, Inc.,
19
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "geis_config.h"
22
#include "geis_backend_multiplexor.h"
26
#include "geis_logging.h"
29
#include <sys/epoll.h>
33
typedef struct CallbackInfo *CallbackInfo;
35
static const GeisSize cbib_initial_size = 4;
36
static const GeisFloat cbib_expansion_factor = 1.5;
41
GeisBackendMultiplexorActivity activity;
42
GeisBackendFdEventCallback callback;
47
typedef struct CallbackInfoBag
55
struct _GeisBackendMultiplexor
58
int mx_max_events_per_pump;
59
CallbackInfoBag mx_callback_infos;
64
* Creates a new container for callback info.
66
static CallbackInfoBag
67
_callback_info_bag_new()
69
CallbackInfoBag cbib = calloc(1, sizeof(struct CallbackInfoBag));
72
geis_error("error allocating Callback Info bag.");
79
* Destroys a callback info container.
82
_callback_info_bag_delete(CallbackInfoBag cbib)
85
CallbackInfo cbi = cbib->pool;
88
CallbackInfo next = cbi->next;
93
/* Dump the bag contents. */
97
CallbackInfo next = cbi->next;
107
* Allocates a CallbackInfo.
110
_callback_info_bag_alloc(CallbackInfoBag cbib,
112
GeisBackendMultiplexorActivity activity,
113
GeisBackendFdEventCallback callback,
116
CallbackInfo callback_info = NULL;
118
/* Either pull a free cbi from the pool or allocate a new one. */
121
callback_info = cbib->pool;
122
cbib->pool = callback_info->next;
126
callback_info = calloc(1, sizeof(struct CallbackInfo));
129
geis_error("error allocating CallbackInfoBag");
134
/* Copy the stuff in. */
135
callback_info->fd = fd;
136
callback_info->activity = activity;
137
callback_info->callback = callback;
138
callback_info->context = context;
140
/* Add it to the in-use list. */
143
cbib->front = callback_info;
147
cbib->back->next = callback_info;
149
cbib->back = callback_info;
152
return callback_info;
157
* Finds a CallbackInfo by file descriptor.
160
_callback_info_bag_find_by_fd(CallbackInfoBag cbib, int fd)
162
CallbackInfo callback_info = NULL;
163
for (callback_info = cbib->front;
165
callback_info = callback_info->next)
167
if (callback_info->fd == fd)
172
return callback_info;
177
* Deallocates a CallbackInfo.
180
_callback_info_bag_release(CallbackInfoBag cbib, int fd)
182
for (CallbackInfo callback_info = cbib->front, prev = NULL;
184
prev = callback_info, callback_info = callback_info->next)
186
if (callback_info->fd == fd)
188
if (callback_info == cbib->front)
190
cbib->front = callback_info->next;
194
prev->next = callback_info->next;
196
if (callback_info == cbib->back)
201
callback_info->next = cbib->pool;
202
cbib->pool = callback_info;
213
* Creates a new backend multiplexor.
215
GeisBackendMultiplexor
216
geis_backend_multiplexor_new()
218
GeisBackendMultiplexor mx = calloc(1, sizeof(struct _GeisBackendMultiplexor));
221
geis_error("failed to allocate backend multiplexor");
225
mx->mx_fd = epoll_create(5);
228
geis_error("error %d creating backend multiplexor: %s",
229
errno, strerror(errno));
232
if (fcntl(mx->mx_fd, F_SETFD, FD_CLOEXEC) < 0)
234
geis_error("error %d setting close-on-exec flag: %s",
235
errno, strerror(errno));
238
mx->mx_max_events_per_pump = GEIS_BE_MX_DEFAULT_EVENTS_PER_PUMP;
240
mx->mx_callback_infos = _callback_info_bag_new();
241
if (!mx->mx_callback_infos)
243
geis_error("failed to allocate backend multiplexor callback_infos");
260
* Destroys an backend multiplexor.
263
geis_backend_multiplexor_delete(GeisBackendMultiplexor mx)
265
_callback_info_bag_delete(mx->mx_callback_infos);
272
_epoll_events_from_activity(GeisBackendMultiplexorActivity activity)
275
if (activity & GEIS_BE_MX_READ_AVAILABLE) events |= EPOLLIN;
276
if (activity & GEIS_BE_MX_WRITE_AVAILABLE) events |= EPOLLOUT;
282
* Adds a file descriptor to an backend multiplexor.
285
geis_backend_multiplexor_add_fd(GeisBackendMultiplexor mx,
287
GeisBackendMultiplexorActivity activity,
288
GeisBackendFdEventCallback callback,
291
CallbackInfo callback_info = _callback_info_bag_alloc(mx->mx_callback_infos,
297
struct epoll_event ev;
298
ev.events = _epoll_events_from_activity(activity);
299
ev.data.ptr = callback_info;
301
int status = epoll_ctl(mx->mx_fd, EPOLL_CTL_ADD, fd, &ev);
304
geis_error("error %d multiplexing fd %d: %s",
305
errno, fd, strerror(errno));
311
* Modifies the activities being monitored on a file descriptor.
314
geis_backend_multiplexor_modify_fd(GeisBackendMultiplexor mx,
316
GeisBackendMultiplexorActivity activity)
319
struct epoll_event ev;
320
CallbackInfo callback_info;
322
callback_info = _callback_info_bag_find_by_fd(mx->mx_callback_infos, fd);
323
callback_info->activity = activity;
325
ev.events = _epoll_events_from_activity(activity);
326
ev.data.ptr = callback_info;
327
status = epoll_ctl(mx->mx_fd, EPOLL_CTL_MOD, fd, &ev);
330
geis_error("error %d remultiplexing fd %d: %s",
331
errno, fd, strerror(errno));
337
* Removes a file descriptor from a backend multiplexor.
339
* @todo free callback_info
342
geis_backend_multiplexor_remove_fd(GeisBackendMultiplexor mx, int fd)
344
_callback_info_bag_release(mx->mx_callback_infos, fd);
345
int status = epoll_ctl(mx->mx_fd, EPOLL_CTL_DEL, fd, NULL);
348
geis_error("error %d demultiplexing fd %d: %s",
349
errno, fd, strerror(errno));
355
* Gets the single file descriptor of the backend multiplexor itself.
358
geis_backend_multiplexor_fd(GeisBackendMultiplexor mx)
365
* gets the maximum number of fd events per pump.
368
geis_backend_multiplexor_max_events_per_pump(GeisBackendMultiplexor mx)
370
return mx->mx_max_events_per_pump;
375
* Sets the maximum number of fd events processed per pump.
378
geis_backend_multiplexor_set_max_events_per_pump(GeisBackendMultiplexor mx,
379
int max_events_per_pump)
381
mx->mx_max_events_per_pump = max_events_per_pump;
386
* Dispatches events on the multiplexed file descriptors.
389
geis_backend_multiplexor_pump(GeisBackendMultiplexor mx)
391
GeisStatus status = GEIS_STATUS_UNKNOWN_ERROR;
392
int processed_event_count = 0;
393
int available_event_count = 1;
394
struct epoll_event events[4];
396
while (available_event_count > 0
397
&& processed_event_count < mx->mx_max_events_per_pump)
399
available_event_count = epoll_wait(mx->mx_fd, events, 4, 0);
400
if (available_event_count < 0)
402
geis_error("error %d in epoll_wait: %s", errno, strerror(errno));
406
for (int i = 0; i < available_event_count; ++i)
408
GeisBackendMultiplexorActivity flags = 0;
409
if (events[i].events & EPOLLIN) flags |= GEIS_BE_MX_READ_AVAILABLE;
410
if (events[i].events & EPOLLOUT) flags |= GEIS_BE_MX_WRITE_AVAILABLE;
411
if (events[i].events & EPOLLHUP) flags |= GEIS_BE_MX_HANGUP_DETECTED;
412
if (events[i].events & EPOLLERR) flags |= GEIS_BE_MX_ERROR_DETECTED;
414
CallbackInfo callback_info = (CallbackInfo)events[i].data.ptr;
415
geis_debug("activity 0x%x on fd %d callback_info=%p", events[i].events, callback_info->fd, (void *)callback_info);
416
callback_info->callback(callback_info->fd, flags, callback_info->context);
417
++processed_event_count;
420
if (available_event_count)
421
status = GEIS_STATUS_CONTINUE;
423
status = GEIS_STATUS_SUCCESS;