~matttbe/ubuntu/raring/geis/lp1077376

« back to all changes in this revision

Viewing changes to libgeis/geis_backend_multiplexor.c

  • Committer: Package Import Robot
  • Author(s): Chase Douglas
  • Date: 2012-07-30 08:51:42 UTC
  • Revision ID: package-import@ubuntu.com-20120730085142-jrc33ygjvt0ob1wl
Tags: upstream-2.2.11
ImportĀ upstreamĀ versionĀ 2.2.11

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @file geis_backend_multiplexor.c
 
3
 * @brief internal GEIS backend multiplexor implementation
 
4
 *
 
5
 * Copyright 2010, 2012 Canonical Ltd.
 
6
 *
 
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
 
10
 * later version.
 
11
 *
 
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
 
15
 * details.
 
16
 *
 
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
 
20
 */
 
21
#include "geis_config.h"
 
22
#include "geis_backend_multiplexor.h"
 
23
 
 
24
#include <errno.h>
 
25
#include <fcntl.h>
 
26
#include "geis_logging.h"
 
27
#include <stdlib.h>
 
28
#include <string.h>
 
29
#include <sys/epoll.h>
 
30
#include <unistd.h>
 
31
 
 
32
 
 
33
typedef struct CallbackInfo *CallbackInfo;
 
34
 
 
35
static const GeisSize  cbib_initial_size = 4;
 
36
static const GeisFloat cbib_expansion_factor = 1.5;
 
37
 
 
38
struct CallbackInfo
 
39
{
 
40
  int                             fd;
 
41
  GeisBackendMultiplexorActivity  activity;
 
42
  GeisBackendFdEventCallback      callback;
 
43
  void                           *context;
 
44
  CallbackInfo                    next;
 
45
};
 
46
 
 
47
typedef struct CallbackInfoBag
 
48
{
 
49
  CallbackInfo front;
 
50
  CallbackInfo back;
 
51
  CallbackInfo pool;
 
52
} *CallbackInfoBag;
 
53
 
 
54
 
 
55
struct _GeisBackendMultiplexor
 
56
{
 
57
  int             mx_fd;
 
58
  int             mx_max_events_per_pump;
 
59
  CallbackInfoBag mx_callback_infos;
 
60
};
 
61
 
 
62
 
 
63
/*
 
64
 * Creates a new container for callback info.
 
65
 */
 
66
static CallbackInfoBag
 
67
_callback_info_bag_new()
 
68
{
 
69
  CallbackInfoBag cbib = calloc(1, sizeof(struct CallbackInfoBag));
 
70
  if (!cbib)
 
71
  {
 
72
    geis_error("error allocating Callback Info bag.");
 
73
  }
 
74
  return cbib;
 
75
}
 
76
 
 
77
 
 
78
/*
 
79
 * Destroys a callback info container.
 
80
 */
 
81
static void
 
82
_callback_info_bag_delete(CallbackInfoBag cbib)
 
83
{
 
84
  /* Drain the pool. */
 
85
  CallbackInfo cbi = cbib->pool;
 
86
  while (cbi)
 
87
  {
 
88
    CallbackInfo next = cbi->next;
 
89
    free(cbi);
 
90
    cbi = next;
 
91
  }
 
92
 
 
93
  /* Dump the bag contents. */
 
94
  cbi = cbib->front;
 
95
  while (cbi)
 
96
  {
 
97
    CallbackInfo next = cbi->next;
 
98
    free(cbi);
 
99
    cbi = next;
 
100
  }
 
101
 
 
102
  free(cbib);
 
103
}
 
104
 
 
105
 
 
106
/*
 
107
 * Allocates a CallbackInfo.
 
108
 */
 
109
static CallbackInfo
 
110
_callback_info_bag_alloc(CallbackInfoBag                 cbib,
 
111
                         int                             fd,
 
112
                         GeisBackendMultiplexorActivity  activity,
 
113
                         GeisBackendFdEventCallback      callback,
 
114
                         void                           *context)
 
115
{
 
116
  CallbackInfo callback_info = NULL;
 
117
 
 
118
  /* Either pull a free cbi from the pool or allocate a new one. */
 
119
  if (cbib->pool)
 
120
  {
 
121
    callback_info = cbib->pool;
 
122
    cbib->pool = callback_info->next;
 
123
  }
 
124
  else
 
125
  {
 
126
    callback_info = calloc(1, sizeof(struct CallbackInfo));
 
127
    if (!callback_info)
 
128
    {
 
129
      geis_error("error allocating CallbackInfoBag");
 
130
      goto final_exit;
 
131
    }
 
132
  }
 
133
 
 
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;
 
139
 
 
140
  /* Add it to the in-use list. */
 
141
  if (!cbib->front)
 
142
  {
 
143
    cbib->front = callback_info;
 
144
  }
 
145
  if (cbib->back)
 
146
  {
 
147
    cbib->back->next = callback_info;
 
148
  }
 
149
  cbib->back = callback_info;
 
150
 
 
151
final_exit:
 
152
  return callback_info;
 
153
}
 
154
 
 
155
 
 
156
/*
 
157
 * Finds a CallbackInfo by file descriptor.
 
158
 */
 
159
CallbackInfo
 
160
_callback_info_bag_find_by_fd(CallbackInfoBag cbib, int fd)
 
161
{
 
162
  CallbackInfo callback_info = NULL;
 
163
  for (callback_info = cbib->front;
 
164
       callback_info;
 
165
       callback_info = callback_info->next)
 
166
  {
 
167
    if (callback_info->fd == fd)
 
168
    {
 
169
      break;
 
170
    }
 
171
  }
 
172
  return callback_info;
 
173
}
 
174
 
 
175
 
 
176
/*
 
177
 * Deallocates a CallbackInfo.
 
178
 */
 
179
static void
 
180
_callback_info_bag_release(CallbackInfoBag cbib, int fd)
 
181
{
 
182
  for (CallbackInfo callback_info = cbib->front, prev = NULL;
 
183
       callback_info;
 
184
       prev = callback_info, callback_info = callback_info->next)
 
185
  {
 
186
    if (callback_info->fd == fd)
 
187
    {
 
188
      if (callback_info == cbib->front)
 
189
      {
 
190
        cbib->front = callback_info->next;
 
191
      }
 
192
      else
 
193
      {
 
194
        prev->next = callback_info->next;
 
195
      }
 
196
      if (callback_info == cbib->back)
 
197
      {
 
198
        cbib->back = prev;
 
199
      }
 
200
 
 
201
      callback_info->next = cbib->pool;
 
202
      cbib->pool = callback_info;
 
203
 
 
204
      break;
 
205
    }
 
206
  }
 
207
}
 
208
 
 
209
 
 
210
 
 
211
 
 
212
/**
 
213
 * Creates a new backend multiplexor.
 
214
 */
 
215
GeisBackendMultiplexor
 
216
geis_backend_multiplexor_new()
 
217
{
 
218
  GeisBackendMultiplexor mx = calloc(1, sizeof(struct _GeisBackendMultiplexor));
 
219
  if (!mx)
 
220
  {
 
221
    geis_error("failed to allocate backend multiplexor");
 
222
  }
 
223
  else
 
224
  {
 
225
    mx->mx_fd = epoll_create(5);
 
226
    if (mx->mx_fd < 0)
 
227
    {
 
228
      geis_error("error %d creating backend multiplexor: %s",
 
229
                 errno, strerror(errno));
 
230
      goto unwind_mx;
 
231
    }
 
232
    if (fcntl(mx->mx_fd, F_SETFD, FD_CLOEXEC) < 0)
 
233
    {
 
234
      geis_error("error %d setting close-on-exec flag: %s",
 
235
                 errno, strerror(errno));
 
236
    }
 
237
 
 
238
    mx->mx_max_events_per_pump = GEIS_BE_MX_DEFAULT_EVENTS_PER_PUMP;
 
239
 
 
240
    mx->mx_callback_infos = _callback_info_bag_new();
 
241
    if (!mx->mx_callback_infos)
 
242
    {
 
243
      geis_error("failed to allocate backend multiplexor callback_infos");
 
244
      goto unwind_epoll;
 
245
    }
 
246
  }
 
247
  goto final_exit;
 
248
 
 
249
unwind_epoll:
 
250
  close(mx->mx_fd);
 
251
unwind_mx:
 
252
  free(mx);
 
253
  mx = NULL;
 
254
final_exit:
 
255
  return mx;
 
256
}
 
257
 
 
258
 
 
259
/**
 
260
 * Destroys an backend multiplexor.
 
261
 */
 
262
void
 
263
geis_backend_multiplexor_delete(GeisBackendMultiplexor mx)
 
264
{
 
265
  _callback_info_bag_delete(mx->mx_callback_infos);
 
266
  close(mx->mx_fd);
 
267
  free(mx);
 
268
}
 
269
 
 
270
 
 
271
static uint32_t
 
272
_epoll_events_from_activity(GeisBackendMultiplexorActivity activity)
 
273
{
 
274
  uint32_t events = 0;
 
275
  if (activity & GEIS_BE_MX_READ_AVAILABLE) events |= EPOLLIN;
 
276
  if (activity & GEIS_BE_MX_WRITE_AVAILABLE) events |= EPOLLOUT;
 
277
  return events;
 
278
}
 
279
 
 
280
 
 
281
/*
 
282
 * Adds a file descriptor to an backend multiplexor.
 
283
 */
 
284
void
 
285
geis_backend_multiplexor_add_fd(GeisBackendMultiplexor          mx,
 
286
                                int                             fd,
 
287
                                GeisBackendMultiplexorActivity  activity,
 
288
                                GeisBackendFdEventCallback      callback,
 
289
                                void                           *context)
 
290
{
 
291
  CallbackInfo callback_info = _callback_info_bag_alloc(mx->mx_callback_infos,
 
292
                                                        fd,
 
293
                                                        activity,
 
294
                                                        callback,
 
295
                                                        context);
 
296
 
 
297
  struct epoll_event ev;
 
298
  ev.events = _epoll_events_from_activity(activity);
 
299
  ev.data.ptr = callback_info;
 
300
   
 
301
  int status = epoll_ctl(mx->mx_fd, EPOLL_CTL_ADD, fd, &ev);
 
302
  if (status < 0)
 
303
  {
 
304
    geis_error("error %d multiplexing fd %d: %s",
 
305
               errno, fd, strerror(errno));
 
306
  }
 
307
}
 
308
 
 
309
 
 
310
/*
 
311
 * Modifies the activities being monitored on a file descriptor.
 
312
 */
 
313
void
 
314
geis_backend_multiplexor_modify_fd(GeisBackendMultiplexor          mx,
 
315
                                   int                             fd,
 
316
                                   GeisBackendMultiplexorActivity  activity)
 
317
{
 
318
  int status;
 
319
  struct epoll_event ev;
 
320
  CallbackInfo callback_info;
 
321
 
 
322
  callback_info = _callback_info_bag_find_by_fd(mx->mx_callback_infos, fd);
 
323
  callback_info->activity = activity;
 
324
 
 
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);
 
328
  if (status < 0)
 
329
  {
 
330
    geis_error("error %d remultiplexing fd %d: %s",
 
331
               errno, fd, strerror(errno));
 
332
  }
 
333
}
 
334
 
 
335
 
 
336
/**
 
337
 * Removes a file descriptor from a backend multiplexor.
 
338
 *
 
339
 * @todo free callback_info
 
340
 */
 
341
void
 
342
geis_backend_multiplexor_remove_fd(GeisBackendMultiplexor mx, int fd)
 
343
{
 
344
  _callback_info_bag_release(mx->mx_callback_infos, fd);
 
345
  int status = epoll_ctl(mx->mx_fd, EPOLL_CTL_DEL, fd, NULL);
 
346
  if (status < 0)
 
347
  {
 
348
    geis_error("error %d demultiplexing fd %d: %s",
 
349
               errno, fd, strerror(errno));
 
350
  }
 
351
}
 
352
 
 
353
 
 
354
/**
 
355
 * Gets the single file descriptor of the backend multiplexor itself.
 
356
 */
 
357
int
 
358
geis_backend_multiplexor_fd(GeisBackendMultiplexor mx)
 
359
{
 
360
  return mx->mx_fd;
 
361
}
 
362
 
 
363
 
 
364
/**
 
365
 * gets the maximum number of fd events per pump.
 
366
 */
 
367
int
 
368
geis_backend_multiplexor_max_events_per_pump(GeisBackendMultiplexor mx)
 
369
{
 
370
  return mx->mx_max_events_per_pump;
 
371
}
 
372
 
 
373
 
 
374
/**
 
375
 * Sets the maximum number of fd events processed per pump.
 
376
 */
 
377
void
 
378
geis_backend_multiplexor_set_max_events_per_pump(GeisBackendMultiplexor mx,
 
379
                                                 int max_events_per_pump)
 
380
{
 
381
  mx->mx_max_events_per_pump = max_events_per_pump;
 
382
}
 
383
 
 
384
 
 
385
/**
 
386
 * Dispatches events on the multiplexed file descriptors.
 
387
 */
 
388
GeisStatus
 
389
geis_backend_multiplexor_pump(GeisBackendMultiplexor mx)
 
390
{
 
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];
 
395
 
 
396
  while (available_event_count > 0
 
397
      && processed_event_count < mx->mx_max_events_per_pump)
 
398
  {
 
399
    available_event_count = epoll_wait(mx->mx_fd, events, 4, 0);
 
400
    if (available_event_count < 0)
 
401
    {
 
402
      geis_error("error %d in epoll_wait: %s", errno, strerror(errno));
 
403
      goto error_exit;
 
404
    }
 
405
 
 
406
    for (int i = 0; i < available_event_count; ++i)
 
407
    {
 
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;
 
413
 
 
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;
 
418
    }
 
419
  }
 
420
  if (available_event_count)
 
421
    status = GEIS_STATUS_CONTINUE;
 
422
  else
 
423
    status = GEIS_STATUS_SUCCESS;
 
424
 
 
425
error_exit:
 
426
  return status;
 
427
}
 
428