~ubuntu-branches/ubuntu/oneiric/libtorrent/oneiric

« back to all changes in this revision

Viewing changes to src/torrent/poll_kqueue.cc

  • Committer: Bazaar Package Importer
  • Author(s): Rogério Brito
  • Date: 2011-03-20 01:06:18 UTC
  • mfrom: (1.1.13 upstream) (4.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110320010618-g3wyylccqzqko73c
Tags: 0.12.7-5
* Use Steinar's "real" patch for IPv6. Addresses #490277, #618275,
  and Closes: #617791.
* Adapt libtorrent-0.12.6-ipv6-07.patch. It FTBFS otherwise.
* Add proper attibution to the IPv6 patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libTorrent - BitTorrent library
 
2
// Copyright (C) 2005-2007, Jari Sundell
 
3
//
 
4
// This program is free software; you can redistribute it and/or modify
 
5
// it under the terms of the GNU General Public License as published by
 
6
// the Free Software Foundation; either version 2 of the License, or
 
7
// (at your option) any later version.
 
8
// 
 
9
// This program is distributed in the hope that it will be useful,
 
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
// GNU General Public License for more details.
 
13
// 
 
14
// You should have received a copy of the GNU General Public License
 
15
// along with this program; if not, write to the Free Software
 
16
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
//
 
18
// In addition, as a special exception, the copyright holders give
 
19
// permission to link the code of portions of this program with the
 
20
// OpenSSL library under certain conditions as described in each
 
21
// individual source file, and distribute linked combinations
 
22
// including the two.
 
23
//
 
24
// You must obey the GNU General Public License in all respects for
 
25
// all of the code used other than OpenSSL.  If you modify file(s)
 
26
// with this exception, you may extend this exception to your version
 
27
// of the file(s), but you are not obligated to do so.  If you do not
 
28
// wish to do so, delete this exception statement from your version.
 
29
// If you delete this exception statement from all source files in the
 
30
// program, then also delete it here.
 
31
//
 
32
// Contact:  Jari Sundell <jaris@ifi.uio.no>
 
33
//
 
34
//           Skomakerveien 33
 
35
//           3185 Skoppum, NORWAY
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#include <cerrno>
 
40
 
 
41
#include <algorithm>
 
42
#include <unistd.h>
 
43
#include <rak/error_number.h>
 
44
#include <rak/functional.h>
 
45
#include <torrent/exceptions.h>
 
46
#include <torrent/event.h>
 
47
 
 
48
#include "poll_kqueue.h"
 
49
#include "thread_base.h"
 
50
 
 
51
#ifdef USE_KQUEUE
 
52
#include <sys/types.h>
 
53
#include <sys/event.h>
 
54
#include <sys/select.h>
 
55
#include <sys/time.h>
 
56
#endif
 
57
 
 
58
#include <assert.h>
 
59
 
 
60
namespace torrent {
 
61
 
 
62
#ifdef USE_KQUEUE
 
63
 
 
64
inline uint32_t
 
65
PollKQueue::event_mask(Event* e) {
 
66
  assert(e->file_descriptor() != -1);
 
67
 
 
68
  Table::value_type entry = m_table[e->file_descriptor()];
 
69
  return entry.second != e ? 0 : entry.first;
 
70
}
 
71
 
 
72
inline void
 
73
PollKQueue::set_event_mask(Event* e, uint32_t m) {
 
74
  assert(e->file_descriptor() != -1);
 
75
 
 
76
  m_table[e->file_descriptor()] = Table::value_type(m, e);
 
77
}
 
78
 
 
79
void
 
80
PollKQueue::flush_events() {
 
81
  timespec timeout = { 0, 0 };
 
82
 
 
83
  int nfds = kevent(m_fd, m_changes, m_changedEvents, m_events + m_waitingEvents, m_maxEvents - m_waitingEvents, &timeout);
 
84
  if (nfds == -1)
 
85
    throw internal_error("PollKQueue::flush_events() error: " + std::string(rak::error_number::current().c_str()));
 
86
 
 
87
  m_changedEvents = 0;
 
88
  m_waitingEvents += nfds;
 
89
}
 
90
 
 
91
void
 
92
PollKQueue::modify(Event* event, unsigned short op, short mask) {
 
93
  // Flush the changed filters to the kernel if the buffer is full.
 
94
  if (m_changedEvents == m_maxEvents) {
 
95
    if (kevent(m_fd, m_changes, m_changedEvents, NULL, 0, NULL) == -1)
 
96
      throw internal_error("PollKQueue::modify() error: " + std::string(rak::error_number::current().c_str()));
 
97
 
 
98
    m_changedEvents = 0;
 
99
  }
 
100
 
 
101
  struct kevent* itr = m_changes + (m_changedEvents++);
 
102
 
 
103
  assert(event == m_table[event->file_descriptor()].second);
 
104
  EV_SET(itr, event->file_descriptor(), mask, op, 0, 0, NULL);
 
105
}
 
106
 
 
107
PollKQueue*
 
108
PollKQueue::create(int maxOpenSockets) {
 
109
  int fd = kqueue();
 
110
 
 
111
  if (fd == -1)
 
112
    return NULL;
 
113
 
 
114
  return new PollKQueue(fd, 1024, maxOpenSockets);
 
115
}
 
116
 
 
117
PollKQueue::PollKQueue(int fd, int maxEvents, int maxOpenSockets) :
 
118
  m_fd(fd),
 
119
  m_maxEvents(maxEvents),
 
120
  m_waitingEvents(0),
 
121
  m_changedEvents(0),
 
122
  m_stdinEvent(NULL) {
 
123
 
 
124
  m_events = new struct kevent[m_maxEvents];
 
125
  m_changes = new struct kevent[maxOpenSockets];
 
126
 
 
127
  m_table.resize(maxOpenSockets);
 
128
}
 
129
 
 
130
PollKQueue::~PollKQueue() {
 
131
  m_table.clear();
 
132
  delete [] m_events;
 
133
  delete [] m_changes;
 
134
 
 
135
  ::close(m_fd);
 
136
}
 
137
 
 
138
int
 
139
PollKQueue::poll(int msec) {
 
140
#if KQUEUE_SOCKET_ONLY
 
141
  if (m_stdinEvent != NULL) {
 
142
    // Flush all changes to the kqueue poll before we start select
 
143
    // polling, so that they get included.
 
144
    if (m_changedEvents != 0)
 
145
      flush_events();
 
146
 
 
147
    if (poll_select(msec) == -1)
 
148
      return -1;
 
149
 
 
150
    // The timeout was already handled in select().
 
151
    msec = 0;
 
152
  }
 
153
#endif
 
154
 
 
155
  timespec timeout = { msec / 1000, (msec % 1000) * 1000000 };
 
156
 
 
157
  int nfds = kevent(m_fd, m_changes, m_changedEvents, m_events + m_waitingEvents, m_maxEvents - m_waitingEvents, &timeout);
 
158
 
 
159
  // Clear the changed events even on fail as we might have received a
 
160
  // signal or similar, and the changed events have already been
 
161
  // consumed.
 
162
  //
 
163
  // There's a chance a bad changed event could make kevent return -1,
 
164
  // but it won't as long as there is room enough in m_events.
 
165
  m_changedEvents = 0;
 
166
 
 
167
  if (nfds == -1)
 
168
    return -1;
 
169
 
 
170
  m_waitingEvents += nfds;
 
171
 
 
172
  return nfds;
 
173
}
 
174
 
 
175
#if KQUEUE_SOCKET_ONLY
 
176
int
 
177
PollKQueue::poll_select(int msec) {
 
178
  if (m_waitingEvents >= m_maxEvents)
 
179
    return 0;
 
180
 
 
181
  timeval selectTimeout = { msec / 1000, (msec % 1000) * 1000 };
 
182
 
 
183
  // If m_fd isn't the first FD opened by the client and has
 
184
  // a low number, using ::poll() here would perform better.
 
185
  //
 
186
  // This kinda assumes fd_set's internal type is int.
 
187
  int readBuffer[m_fd + 1];
 
188
  fd_set* readSet = (fd_set*)&readBuffer;
 
189
 
 
190
  std::memset(readBuffer, 0, m_fd + 1);
 
191
  FD_SET(0,    readSet);
 
192
  FD_SET(m_fd, readSet);
 
193
 
 
194
  int nfds = select(m_fd + 1, readSet, NULL, NULL, &selectTimeout);
 
195
 
 
196
  if (nfds == -1)
 
197
    return nfds;
 
198
 
 
199
  if (FD_ISSET(0, readSet)) {
 
200
    m_events[m_waitingEvents].ident = 0;
 
201
    m_events[m_waitingEvents].filter = EVFILT_READ;
 
202
    m_events[m_waitingEvents].flags = 0;
 
203
    m_waitingEvents++;
 
204
  }
 
205
 
 
206
  return nfds;
 
207
}
 
208
#endif
 
209
 
 
210
void
 
211
PollKQueue::perform() {
 
212
  for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) {
 
213
    if (itr->ident < 0 || itr->ident >= m_table.size())
 
214
      continue;
 
215
 
 
216
    if ((flags() & flag_waive_global_lock) && ThreadBase::global_queue_size() != 0)
 
217
      ThreadBase::waive_global_lock();
 
218
 
 
219
    Table::iterator evItr = m_table.begin() + itr->ident;
 
220
 
 
221
    if ((itr->flags & EV_ERROR) && evItr->second != NULL) {
 
222
      if (evItr->first & flag_error)
 
223
        evItr->second->event_error();
 
224
      continue;
 
225
    }
 
226
 
 
227
    // Also check current mask.
 
228
 
 
229
    if (itr->filter == EVFILT_READ && evItr->second != NULL && evItr->first & flag_read)
 
230
      evItr->second->event_read();
 
231
 
 
232
    if (itr->filter == EVFILT_WRITE && evItr->second != NULL && evItr->first & flag_write)
 
233
      evItr->second->event_write();
 
234
  }
 
235
 
 
236
  m_waitingEvents = 0;
 
237
}
 
238
 
 
239
uint32_t
 
240
PollKQueue::open_max() const {
 
241
  return m_table.size();
 
242
}
 
243
 
 
244
void
 
245
PollKQueue::open(Event* event) {
 
246
  if (event_mask(event) != 0)
 
247
    throw internal_error("PollKQueue::open(...) called but the file descriptor is active");
 
248
}
 
249
 
 
250
void
 
251
PollKQueue::close(Event* event) {
 
252
#if KQUEUE_SOCKET_ONLY
 
253
  if (event->file_descriptor() == 0) {
 
254
    m_stdinEvent = NULL;
 
255
    return;
 
256
  }
 
257
#endif
 
258
 
 
259
  if (event_mask(event) != 0)
 
260
    throw internal_error("PollKQueue::close(...) called but the file descriptor is active");
 
261
 
 
262
  m_table[event->file_descriptor()] = Table::value_type();
 
263
 
 
264
  /*
 
265
  Shouldn't be needed anymore.
 
266
  for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr)
 
267
    if (itr->udata == event)
 
268
      itr->udata = NULL;
 
269
 
 
270
  m_changedEvents = std::remove_if(m_changes, m_changes + m_changedEvents, rak::equal(event, rak::mem_ref(&kevent::udata))) - m_changes;
 
271
  */
 
272
}
 
273
 
 
274
void
 
275
PollKQueue::closed(Event* event) {
 
276
#if KQUEUE_SOCKET_ONLY
 
277
  if (event->file_descriptor() == 0) {
 
278
    m_stdinEvent = NULL;
 
279
    return;
 
280
  }
 
281
#endif
 
282
 
 
283
  // Kernel removes closed FDs automatically, so just clear the mask
 
284
  // and remove it from pending calls.  Don't touch if the FD was
 
285
  // re-used before we received the close notification.
 
286
  if (m_table[event->file_descriptor()].second == event)
 
287
    m_table[event->file_descriptor()] = Table::value_type();
 
288
 
 
289
  /*
 
290
  for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) {
 
291
    if (itr->udata == event)
 
292
      itr->udata = NULL;
 
293
  }
 
294
 
 
295
  m_changedEvents = std::remove_if(m_changes, m_changes + m_changedEvents, rak::equal(event, rak::mem_ref(&kevent::udata))) - m_changes;
 
296
  */
 
297
}
 
298
 
 
299
// Use custom defines for EPOLL* to make the below code compile with
 
300
// and with epoll.
 
301
bool
 
302
PollKQueue::in_read(Event* event) {
 
303
  return event_mask(event) & flag_read;
 
304
}
 
305
 
 
306
bool
 
307
PollKQueue::in_write(Event* event) {
 
308
  return event_mask(event) & flag_write;
 
309
}
 
310
 
 
311
bool
 
312
PollKQueue::in_error(Event* event) {
 
313
  return event_mask(event) & flag_error;
 
314
}
 
315
 
 
316
void
 
317
PollKQueue::insert_read(Event* event) {
 
318
  if (event_mask(event) & flag_read)
 
319
    return;
 
320
 
 
321
  set_event_mask(event, event_mask(event) | flag_read);
 
322
 
 
323
#if KQUEUE_SOCKET_ONLY
 
324
  if (event->file_descriptor() == 0) {
 
325
    m_stdinEvent = event;
 
326
    return;
 
327
  }
 
328
#endif
 
329
 
 
330
  modify(event, EV_ADD, EVFILT_READ);
 
331
}
 
332
 
 
333
void
 
334
PollKQueue::insert_write(Event* event) {
 
335
  if (event_mask(event) & flag_write)
 
336
    return;
 
337
 
 
338
  set_event_mask(event, event_mask(event) | flag_write);
 
339
  modify(event, EV_ADD, EVFILT_WRITE);
 
340
}
 
341
 
 
342
void
 
343
PollKQueue::insert_error(Event* event) {
 
344
}
 
345
 
 
346
void
 
347
PollKQueue::remove_read(Event* event) {
 
348
  if (!(event_mask(event) & flag_read))
 
349
    return;
 
350
 
 
351
  set_event_mask(event, event_mask(event) & ~flag_read);
 
352
 
 
353
#if KQUEUE_SOCKET_ONLY
 
354
  if (event->file_descriptor() == 0) {
 
355
    m_stdinEvent = NULL;
 
356
    return;
 
357
  }
 
358
#endif
 
359
 
 
360
  modify(event, EV_DELETE, EVFILT_READ);
 
361
}
 
362
 
 
363
void
 
364
PollKQueue::remove_write(Event* event) {
 
365
  if (!(event_mask(event) & flag_write))
 
366
    return;
 
367
 
 
368
  set_event_mask(event, event_mask(event) & ~flag_write);
 
369
  modify(event, EV_DELETE, EVFILT_WRITE);
 
370
}
 
371
 
 
372
void
 
373
PollKQueue::remove_error(Event* event) {
 
374
}
 
375
 
 
376
#else // USE_QUEUE
 
377
 
 
378
PollKQueue*
 
379
PollKQueue::create(__UNUSED int maxOpenSockets) {
 
380
  return NULL;
 
381
}
 
382
 
 
383
PollKQueue::~PollKQueue() {
 
384
}
 
385
 
 
386
int
 
387
PollKQueue::poll(__UNUSED int msec) {
 
388
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
389
}
 
390
 
 
391
void
 
392
PollKQueue::perform() {
 
393
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
394
}
 
395
 
 
396
uint32_t
 
397
PollKQueue::open_max() const {
 
398
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
399
}
 
400
 
 
401
void
 
402
PollKQueue::open(__UNUSED torrent::Event* event) {
 
403
}
 
404
 
 
405
void
 
406
PollKQueue::close(__UNUSED torrent::Event* event) {
 
407
}
 
408
 
 
409
void
 
410
PollKQueue::closed(__UNUSED torrent::Event* event) {
 
411
}
 
412
 
 
413
bool
 
414
PollKQueue::in_read(__UNUSED torrent::Event* event) {
 
415
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
416
}
 
417
 
 
418
bool
 
419
PollKQueue::in_write(__UNUSED torrent::Event* event) {
 
420
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
421
}
 
422
 
 
423
bool
 
424
PollKQueue::in_error(__UNUSED torrent::Event* event) {
 
425
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
426
}
 
427
 
 
428
void
 
429
PollKQueue::insert_read(__UNUSED torrent::Event* event) {
 
430
}
 
431
 
 
432
void
 
433
PollKQueue::insert_write(__UNUSED torrent::Event* event) {
 
434
}
 
435
 
 
436
void
 
437
PollKQueue::insert_error(__UNUSED torrent::Event* event) {
 
438
}
 
439
 
 
440
void
 
441
PollKQueue::remove_read(__UNUSED torrent::Event* event) {
 
442
}
 
443
 
 
444
void
 
445
PollKQueue::remove_write(__UNUSED torrent::Event* event) {
 
446
}
 
447
 
 
448
void
 
449
PollKQueue::remove_error(__UNUSED torrent::Event* event) {
 
450
}
 
451
 
 
452
PollKQueue::PollKQueue(__UNUSED int fd, __UNUSED int maxEvents, __UNUSED int maxOpenSockets) {
 
453
  throw internal_error("An PollKQueue function was called, but it is disabled.");
 
454
}
 
455
 
 
456
#endif // USE_KQUEUE
 
457
 
 
458
}