patches/rtorrent-03-curl-event.diff
changeset 1248 9f6e7108cd7f
child 1263 74f1e78b1e06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/rtorrent-03-curl-event.diff	Sat Jun 28 22:44:14 2008 +0000
@@ -0,0 +1,520 @@
+diff -urN rtorrent-0.8.2.orig/src/core/curl_event.cc rtorrent-0.8.2/src/core/curl_event.cc
+--- rtorrent-0.8.2.orig/src/core/curl_event.cc	1969-12-31 19:00:00.000000000 -0500
++++ rtorrent-0.8.2/src/core/curl_event.cc	2008-06-10 14:40:41.403064000 -0400
+@@ -0,0 +1,63 @@
++// libTorrent - BitTorrent library
++// Copyright (C) 2008 Albert Lee
++//
++// This program is free software; you can redistribute it and/or modify
++// it under the terms of the GNU General Public License as published by
++// the Free Software Foundation; either version 2 of the License, or
++// (at your option) any later version.
++// 
++// This program is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++// GNU General Public License for more details.
++// 
++// You should have received a copy of the GNU General Public License
++// along with this program; if not, write to the Free Software
++// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++//
++// In addition, as a special exception, the copyright holders give
++// permission to link the code of portions of this program with the
++// OpenSSL library under certain conditions as described in each
++// individual source file, and distribute linked combinations
++// including the two.
++//
++// You must obey the GNU General Public License in all respects for
++// all of the code used other than OpenSSL.  If you modify file(s)
++// with this exception, you may extend this exception to your version
++// of the file(s), but you are not obligated to do so.  If you do not
++// wish to do so, delete this exception statement from your version.
++// If you delete this exception statement from all source files in the
++// program, then also delete it here.
++//
++// Contact:  Jari Sundell <[email protected]>
++//
++//           Skomakerveien 33
++//           3185 Skoppum, NORWAY
++
++#include "config.h"
++
++#include <torrent/exceptions.h>
++
++#include "rak/functional.h"
++
++#include "curl_event.h"
++#include "curl_stack.h"
++
++namespace core {
++
++void
++CurlEvent::event_read() {
++  m_stack->perform(m_fileDesc);
++}
++
++void
++CurlEvent::event_write() {
++  m_stack->perform(m_fileDesc);
++}
++
++void
++CurlEvent::event_error() {
++  m_stack->perform(m_fileDesc);
++}
++
++}
+diff -urN rtorrent-0.8.2.orig/src/core/curl_event.h rtorrent-0.8.2/src/core/curl_event.h
+--- rtorrent-0.8.2.orig/src/core/curl_event.h	1969-12-31 19:00:00.000000000 -0500
++++ rtorrent-0.8.2/src/core/curl_event.h	2008-06-10 14:37:59.758119000 -0400
+@@ -0,0 +1,61 @@
++// libTorrent - BitTorrent library
++// Copyright (C) 2008 Albert Lee
++//
++// This program is free software; you can redistribute it and/or modify
++// it under the terms of the GNU General Public License as published by
++// the Free Software Foundation; either version 2 of the License, or
++// (at your option) any later version.
++// 
++// This program is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++// GNU General Public License for more details.
++// 
++// You should have received a copy of the GNU General Public License
++// along with this program; if not, write to the Free Software
++// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++//
++// In addition, as a special exception, the copyright holders give
++// permission to link the code of portions of this program with the
++// OpenSSL library under certain conditions as described in each
++// individual source file, and distribute linked combinations
++// including the two.
++//
++// You must obey the GNU General Public License in all respects for
++// all of the code used other than OpenSSL.  If you modify file(s)
++// with this exception, you may extend this exception to your version
++// of the file(s), but you are not obligated to do so.  If you do not
++// wish to do so, delete this exception statement from your version.
++// If you delete this exception statement from all source files in the
++// program, then also delete it here.
++//
++// Contact:  Jari Sundell <[email protected]>
++//
++//           Skomakerveien 33
++//           3185 Skoppum, NORWAY
++
++#ifndef RTORRENT_CORE_CURL_EVENT_H
++#define RTORRENT_CORE_CURL_EVENT_H
++
++#include <torrent/event.h>
++
++namespace core {
++
++class CurlStack;
++
++class CurlEvent : public torrent::Event {
++public:
++  CurlEvent(CurlStack* s, int fd) : m_stack(s) { m_fileDesc = fd; }
++  virtual ~CurlEvent() {}
++
++  virtual void        event_read();
++  virtual void        event_write();
++  virtual void        event_error();
++
++protected:
++  CurlStack*          m_stack;
++};
++
++}
++
++#endif
+diff -urN rtorrent-0.8.2.orig/src/core/curl_stack.cc rtorrent-0.8.2/src/core/curl_stack.cc
+--- rtorrent-0.8.2.orig/src/core/curl_stack.cc	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/curl_stack.cc	2008-06-10 14:32:12.992249000 -0400
+@@ -44,13 +44,77 @@
+ #include "rak/functional.h"
+ #include "curl_get.h"
+ #include "curl_stack.h"
++#include "curl_event.h"
++
++#include "poll_manager.h"
+ 
+ namespace core {
+ 
+-CurlStack::CurlStack() :
++#if LIBCURL_VERSION_NUM >= 0x071000
++static void timer_callback(curl_socket_t socket, int action, void* event_data)
++{
++  CurlStack* s = static_cast<CurlStack*>(event_data);
++  CURLM* handle = (CURLM*)(s->handle());
++  CURLMcode rc;
++  int count;
++
++  while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket(handle, CURL_SOCKET_TIMEOUT, &count)));
++}
++#endif
++
++static int socket_callback(CURL *easy, curl_socket_t socket, int action, void* socket_data, void* assign_data)
++{
++  CurlStack* stack = static_cast<CurlStack*>(socket_data);
++  torrent::Event* event = static_cast<torrent::Event*>(assign_data);
++  torrent::Poll* poll = stack->poll();
++
++  if (!event) {
++    event = new CurlEvent(stack, socket);
++    curl_multi_assign((CURLM*)(stack->handle()), socket, event);
++    if (socket > poll->open_max())
++      throw torrent::internal_error("Socket too large for " + poll->open_max());
++    poll->open(event);
++  } else {
++    poll->remove_read(event);
++    poll->remove_write(event);
++    poll->remove_error(event);
++  }
++
++  switch (action) {
++    case CURL_POLL_NONE:
++      break;
++    case CURL_POLL_IN:
++      poll->insert_read(event);
++      break;
++    case CURL_POLL_OUT:
++      poll->insert_write(event);
++      break;
++    case CURL_POLL_INOUT:
++      poll->insert_read(event);
++      poll->insert_write(event);
++      break;
++    case CURL_POLL_REMOVE:
++      poll->close(event);
++      delete event;
++      break;
++    default:
++      throw torrent::internal_error("socket_callback(...) called with unsupported action");
++  }
++
++  return 0;
++}
++
++CurlStack::CurlStack(torrent::Poll* poll) :
+   m_handle((void*)curl_multi_init()),
++  m_poll(poll),
+   m_active(0),
+   m_maxActive(32) {
++  curl_multi_setopt(m_handle, CURLMOPT_SOCKETDATA, this);
++  curl_multi_setopt(m_handle, CURLMOPT_SOCKETFUNCTION, socket_callback);
++#if LIBCURL_VERSION_NUM >= 0x071000
++  curl_multi_setopt(m_handle, CURLMOPT_TIMERDATA, this);
++  curl_multi_setopt(m_handle, CURLMOPT_TIMERFUNCTION, timer_callback);
++#endif
+ }
+ 
+ CurlStack::~CurlStack() {
+@@ -66,35 +130,59 @@
+ }
+ 
+ void
+-CurlStack::perform() {
++CurlStack::process() {
++  int t;
++  CURLMsg* msg;
++
++  while ((msg = curl_multi_info_read((CURLM*)m_handle, &t)) != NULL) {
++    if (msg->msg != CURLMSG_DONE)
++      throw torrent::internal_error("CurlStack::process() msg->msg != CURLMSG_DONE.");
++
++    iterator itr = std::find_if(begin(), end(), rak::equal(msg->easy_handle, std::mem_fun(&CurlGet::handle)));
++
++    if (itr == end())
++      throw torrent::internal_error("Could not find CurlGet with the right easy_handle.");
++        
++    if (msg->data.result == CURLE_OK)
++      (*itr)->signal_done().emit();
++    else
++      (*itr)->signal_failed().emit(curl_easy_strerror(msg->data.result));
++  }
++}
++
++void
++CurlStack::perform(curl_socket_t sockfd) {
+   CURLMcode code;
+ 
+   do {
+     int count;
+-    code = curl_multi_perform((CURLM*)m_handle, &count);
++    code = curl_multi_socket((CURLM*)m_handle, sockfd, &count);
+ 
+     if (code > 0)
+-      throw torrent::internal_error("Error calling curl_multi_perform.");
++      throw torrent::internal_error("Error calling curl_multi_socket.");
+ 
+     if ((unsigned int)count != size()) {
+       // Done with some handles.
+-      int t;
+-      CURLMsg* msg;
++      this->process();
++    }
+ 
+-      while ((msg = curl_multi_info_read((CURLM*)m_handle, &t)) != NULL) {
+-        if (msg->msg != CURLMSG_DONE)
+-          throw torrent::internal_error("CurlStack::perform() msg->msg != CURLMSG_DONE.");
++  } while (code == CURLM_CALL_MULTI_PERFORM);
++}
+ 
+-        iterator itr = std::find_if(begin(), end(), rak::equal(msg->easy_handle, std::mem_fun(&CurlGet::handle)));
++void
++CurlStack::perform() {
++  CURLMcode code;
+ 
+-        if (itr == end())
+-          throw torrent::internal_error("Could not find CurlGet with the right easy_handle.");
+-        
+-        if (msg->data.result == CURLE_OK)
+-          (*itr)->signal_done().emit();
+-        else
+-          (*itr)->signal_failed().emit(curl_easy_strerror(msg->data.result));
+-      }
++  do {
++    int count;
++    code = curl_multi_perform((CURLM*)m_handle, &count);
++
++    if (code > 0)
++      throw torrent::internal_error("Error calling curl_multi_perform.");
++
++    if ((unsigned int)count != size()) {
++      // Done with some handles.
++      this->process();
+     }
+ 
+   } while (code == CURLM_CALL_MULTI_PERFORM);
+diff -urN rtorrent-0.8.2.orig/src/core/curl_stack.h rtorrent-0.8.2/src/core/curl_stack.h
+--- rtorrent-0.8.2.orig/src/core/curl_stack.h	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/curl_stack.h	2008-06-10 13:54:21.695024000 -0400
+@@ -40,11 +40,19 @@
+ #include <deque>
+ #include <string>
+ #include <sigc++/functors/slot.h>
++#include <curl/curl.h>
++
++class torrent::Poll;
+ 
+ namespace core {
+ 
+ class CurlGet;
+ 
++#if LIBCURL_VERSION_NUM >= 0x071000
++static void timer_callback(curl_socket_t socket, int action, void* event_data);
++#endif
++static int socket_callback(CURL *easy, curl_socket_t socket, int action, void* socket_data, void* assign_data);
++
+ // By using a deque instead of vector we allow for cheaper removal of
+ // the oldest elements, those that will be first in the in the
+ // deque.
+@@ -76,16 +84,19 @@
+   using base_type::size;
+   using base_type::empty;
+ 
+-  CurlStack();
++  CurlStack(torrent::Poll* poll);
+   ~CurlStack();
+ 
+   CurlGet*            new_object();
+ 
++  void                perform(curl_socket_t sockfd);
+   void                perform();
+ 
+   // TODO: Set fd_set's only once?
+   unsigned int        fdset(fd_set* readfds, fd_set* writefds, fd_set* exceptfds);
+ 
++  void*               handle()                               { return m_handle; }
++  torrent::Poll*      poll()                                 { return m_poll; }         
+   unsigned int        active() const                         { return m_active; }
+   unsigned int        max_active() const                     { return m_maxActive; }
+   void                set_max_active(unsigned int a)         { m_maxActive = a; }
+@@ -111,12 +122,14 @@
+  protected:
+   void                add_get(CurlGet* get);
+   void                remove_get(CurlGet* get);
++  void                process();
+ 
+  private:
+   CurlStack(const CurlStack&);
+   void operator = (const CurlStack&);
+ 
+   void*               m_handle;
++  torrent::Poll*      m_poll;
+ 
+   unsigned int        m_active;
+   unsigned int        m_maxActive;
+diff -urN rtorrent-0.8.2.orig/src/core/Makefile.am rtorrent-0.8.2/src/core/Makefile.am
+--- rtorrent-0.8.2.orig/src/core/Makefile.am	2008-06-10 15:13:49.786345331 -0400
++++ rtorrent-0.8.2/src/core/Makefile.am	2008-06-10 02:25:32.809721000 -0400
+@@ -1,6 +1,8 @@
+ noinst_LIBRARIES = libsub_core.a
+ 
+ libsub_core_a_SOURCES = \
++	curl_event.cc \
++	curl_event.h \
+ 	curl_get.cc \
+ 	curl_get.h \
+ 	curl_stack.cc \
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager_epoll.cc rtorrent-0.8.2/src/core/poll_manager_epoll.cc
+--- rtorrent-0.8.2.orig/src/core/poll_manager_epoll.cc	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/poll_manager_epoll.cc	2008-06-10 14:17:27.568558000 -0400
+@@ -67,6 +67,7 @@
+   torrent::perform();
+   timeout = std::min(timeout, rak::timer(torrent::next_timeout())) + 1000;
+ 
++#if 0
+   if (!m_httpStack.empty()) {
+     // When we're using libcurl we need to use select, but as this is
+     // inefficient we try avoiding it whenever possible.
+@@ -101,6 +102,7 @@
+     // Clear the timeout since we've already used it in the select call.
+     timeout = rak::timer();
+   }
++#endif
+ 
+   // Yes, below is how much code really *should* have been in this
+   // function. ;)
+@@ -108,6 +110,9 @@
+   if (static_cast<torrent::PollEPoll*>(m_poll)->poll((timeout.usec() + 999) / 1000) == -1)
+     return check_error();
+ 
++  if (!m_httpStack->empty())
++    m_httpStack->perform();
++ 
+   torrent::perform();
+   static_cast<torrent::PollEPoll*>(m_poll)->perform();
+ }
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager_kqueue.cc rtorrent-0.8.2/src/core/poll_manager_kqueue.cc
+--- rtorrent-0.8.2.orig/src/core/poll_manager_kqueue.cc	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/poll_manager_kqueue.cc	2008-06-10 14:17:31.672907000 -0400
+@@ -68,6 +68,7 @@
+   torrent::perform();
+   timeout = std::min(timeout, rak::timer(torrent::next_timeout())) + 1000;
+ 
++#if 0
+   if (!m_httpStack.empty()) {
+     // When we're using libcurl we need to use select, but as this is
+     // inefficient we try avoiding it whenever possible.
+@@ -102,6 +103,7 @@
+     // Clear the timeout since we've already used it in the select call.
+     timeout = rak::timer();
+   }
++#endif
+ 
+   // Yes, below is how much code really *should* have been in this
+   // function. ;)
+@@ -109,6 +111,9 @@
+   if (static_cast<torrent::PollKQueue*>(m_poll)->poll((timeout.usec() + 999) / 1000) == -1)
+     return check_error();
+ 
++  if (!m_httpStack->empty())
++    m_httpStack->perform();
++ 
+   torrent::perform();
+   static_cast<torrent::PollKQueue*>(m_poll)->perform();
+ }
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager_ports.cc rtorrent-0.8.2/src/core/poll_manager_ports.cc
+--- rtorrent-0.8.2.orig/src/core/poll_manager_ports.cc	2008-06-10 15:13:49.790288665 -0400
++++ rtorrent-0.8.2/src/core/poll_manager_ports.cc	2008-06-10 14:31:09.552278000 -0400
+@@ -68,6 +68,7 @@
+   torrent::perform();
+   timeout = std::min(timeout, rak::timer(torrent::next_timeout())) + 1000;
+ 
++#if 0
+   if (!m_httpStack.empty()) {
+     // When we're using libcurl we need to use select, but as this is
+     // inefficient we try avoiding it whenever possible.
+@@ -103,6 +104,7 @@
+     // Clear the timeout since we've already used it in the select call.
+     timeout = rak::timer();
+   }
++#endif
+ 
+   // Yes, below is how much code really *should* have been in this
+   // function. ;)
+@@ -111,6 +113,9 @@
+ 	  std::cerr << "error from ports poll\n";
+     return check_error();
+   }
++  if (!m_httpStack->empty())
++    m_httpStack->perform();
++ 
+   torrent::perform();
+   static_cast<torrent::PollPorts*>(m_poll)->perform();
+ }
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager_select.cc rtorrent-0.8.2/src/core/poll_manager_select.cc
+--- rtorrent-0.8.2.orig/src/core/poll_manager_select.cc	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/poll_manager_select.cc	2008-06-10 02:37:17.419878000 -0400
+@@ -77,16 +77,16 @@
+ 
+   unsigned int maxFd = static_cast<torrent::PollSelect*>(m_poll)->fdset(m_readSet, m_writeSet, m_errorSet);
+ 
+-  if (!m_httpStack.empty())
+-    maxFd = std::max(maxFd, m_httpStack.fdset(m_readSet, m_writeSet, m_errorSet));
++  if (!m_httpStack->empty())
++    maxFd = std::max(maxFd, m_httpStack->fdset(m_readSet, m_writeSet, m_errorSet));
+ 
+   timeval t = timeout.tval();
+ 
+   if (select(maxFd + 1, m_readSet, m_writeSet, m_errorSet, &t) == -1)
+     return check_error();
+ 
+-  if (!m_httpStack.empty())
+-    m_httpStack.perform();
++  if (!m_httpStack->empty())
++    m_httpStack->perform();
+ 
+   torrent::perform();
+   static_cast<torrent::PollSelect*>(m_poll)->perform(m_readSet, m_writeSet, m_errorSet);
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager.cc rtorrent-0.8.2/src/core/poll_manager.cc
+--- rtorrent-0.8.2.orig/src/core/poll_manager.cc	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/poll_manager.cc	2008-06-10 02:35:35.523377000 -0400
+@@ -44,7 +44,8 @@
+ namespace core {
+ 
+ PollManager::PollManager(torrent::Poll* poll) :
+-  m_poll(poll) {
++  m_poll(poll),
++  m_httpStack(new CurlStack(poll)) {
+ 
+   if (m_poll == NULL)
+     throw std::logic_error("PollManager::PollManager(...) received poll == NULL");
+@@ -74,10 +75,11 @@
+ 
+   // Call this so curl has valid fd_set pointers if curl_multi_perform
+   // is called before it gets set when polling.
+-  m_httpStack.fdset(m_readSet, m_writeSet, m_errorSet);
++  m_httpStack->fdset(m_readSet, m_writeSet, m_errorSet);
+ }
+ 
+ PollManager::~PollManager() {
++  delete m_httpStack;
+   delete m_poll;
+ 
+ #if defined USE_VARIABLE_FDSET
+diff -urN rtorrent-0.8.2.orig/src/core/poll_manager.h rtorrent-0.8.2/src/core/poll_manager.h
+--- rtorrent-0.8.2.orig/src/core/poll_manager.h	2008-05-07 08:19:11.000000000 -0400
++++ rtorrent-0.8.2/src/core/poll_manager.h	2008-06-10 02:34:57.898088000 -0400
+@@ -58,7 +58,7 @@
+ 
+   unsigned int        get_open_max() const         { return m_poll->open_max(); }
+ 
+-  CurlStack*          get_http_stack()             { return &m_httpStack; }
++  CurlStack*          get_http_stack()             { return m_httpStack; }
+   torrent::Poll*      get_torrent_poll()           { return m_poll; }
+ 
+   virtual void        poll(rak::timer timeout) = 0;
+@@ -70,7 +70,7 @@
+   void                check_error();
+ 
+   torrent::Poll*      m_poll;
+-  CurlStack           m_httpStack;
++  CurlStack*          m_httpStack;
+ 
+   unsigned int        m_setSize;
+   fd_set*             m_readSet;