Dear all,

I have modified the way curl used in ecore_con_url.c

1) A file descriptor of libcurl is not matched to any easy handles.
We can get a file descriptor from curl_multi_fdset(), we do not
guarantee that it is a socket of easy handle just performed.
If libcurl multi handle uses pipeline, the number of file descriptors
is just 1 or 2 even though the number of easy handles is numerous.
So, I removed the fd, fd_handler, flags from _Ecore_Con_Url structure.

2) There are no routines when return value is CURLM_CALL_MULTI_PERFORM
or still_running is above 1.
When return value is CURLM_CALL_MULTI_PERFORM, we just do
curl_multi_perform() again. (do not curl_multi_fdset() or
curl_multi_info_read() )
In addition, there are case that curl_multi_perform() returns CURLM_OK
but we cannot get fd and read any information.  (e.g., libcurl
threaded resolver enabled)
In such a case, current _ecore_con_url_perform() returns
EINA_FALSE~~!!!. This is a problem.
So, I added routines for each case.


3) Error control should be added.
It is not controlled when curl_multi_perform() returns error.

Please review a attached patch.
Index: src/lib/ecore_con/ecore_con_url.c
===================================================================
--- src/lib/ecore_con/ecore_con_url.c	(리비전 65035)
+++ src/lib/ecore_con/ecore_con_url.c	(작업 사본)
@@ -35,8 +35,6 @@ int ECORE_CON_EVENT_URL_COMPLETE = 0;
 int ECORE_CON_EVENT_URL_PROGRESS = 0;
 
 #ifdef HAVE_CURL
-static Eina_Bool _ecore_con_url_fd_handler(void             *data,
-                                           Ecore_Fd_Handler *fd_handler);
 static Eina_Bool _ecore_con_url_perform(Ecore_Con_Url *url_con);
 static size_t    _ecore_con_url_header_cb(void  *ptr,
                                           size_t size,
@@ -57,12 +55,11 @@ static size_t _ecore_con_url_read_cb(voi
                                      void  *stream);
 static void      _ecore_con_event_url_free(void *data __UNUSED__,
                                            void      *ev);
-static int       _ecore_con_url_process_completed_jobs(
-  Ecore_Con_Url *url_con_to_match);
 static Eina_Bool _ecore_con_url_idler_handler(void *data);
+static Eina_Bool _ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__);
 
-static Ecore_Idler *_fd_idler_handler = NULL;
 static Eina_List *_url_con_list = NULL;
+static Eina_List *_fd_hd_list = NULL;
 static CURLM *_curlm = NULL;
 static fd_set _current_fd_set;
 static int _init_count = 0;
@@ -113,51 +110,30 @@ EAPI int
 ecore_con_url_init(void)
 {
 #ifdef HAVE_CURL
-   _init_count++;
+   if (++_init_count > 1) return _init_count;
 
-   if (_init_count > 1)
-     return _init_count;
-
-   if (!ECORE_CON_EVENT_URL_DATA)
-     {
-        ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
-        ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
-        ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
-     }
+   if (!ECORE_CON_EVENT_URL_DATA)     ECORE_CON_EVENT_URL_DATA = ecore_event_type_new();
+   if (!ECORE_CON_EVENT_URL_COMPLETE) ECORE_CON_EVENT_URL_COMPLETE = ecore_event_type_new();
+   if (!ECORE_CON_EVENT_URL_PROGRESS) ECORE_CON_EVENT_URL_PROGRESS = ecore_event_type_new();
 
    if (!_curlm)
      {
         long ms;
 
-        FD_ZERO(&_current_fd_set);
-        if (curl_global_init(CURL_GLOBAL_ALL))
-          {
-             while (_url_con_list)
-               ecore_con_url_free(eina_list_data_get(_url_con_list));
-             return 0;
-          }
+        // curl_global_init() is not thread safe!
+        if (curl_global_init(CURL_GLOBAL_ALL)) return --_init_count;
 
         _curlm = curl_multi_init();
-        if (!_curlm)
-          {
-             while (_url_con_list)
-               ecore_con_url_free(eina_list_data_get(_url_con_list));
-
-             _init_count--;
-             return 0;
-          }
+        if (!_curlm)  return --_init_count;
 
         curl_multi_timeout(_curlm, &ms);
-        if (ms <= 0)
-          ms = 1000;
+        if (ms <= 0) ms = 100;
 
-        _curl_timeout =
-          ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler,
-                          (void *)0xACE);
+        _curl_timeout = ecore_timer_add((double)ms / 1000, _ecore_con_url_idler_handler, (void *)0xACE);
         ecore_timer_freeze(_curl_timeout);
      }
 
-   return 1;
+   return _init_count;
 #else
    return 0;
 #endif
@@ -167,34 +143,40 @@ EAPI int
 ecore_con_url_shutdown(void)
 {
 #ifdef HAVE_CURL
-   if (!_init_count)
-     return 0;
-
-   _init_count--;
-
-   if (_init_count != 0)
-     return _init_count;
-
-   if (_fd_idler_handler)
-     ecore_idler_del(_fd_idler_handler);
-
-   _fd_idler_handler = NULL;
+   if (_init_count == 0) return 0;
 
-   if (_curl_timeout)
-     ecore_timer_del(_curl_timeout);
-
-   _curl_timeout = NULL;
-
-   while (_url_con_list)
-     ecore_con_url_free(eina_list_data_get(_url_con_list));
-
-   if (_curlm)
+   if (--_init_count == 0)
      {
-        curl_multi_cleanup(_curlm);
-        _curlm = NULL;
-     }
+        if (_curl_timeout)
+          {
+             ecore_timer_del(_curl_timeout);
+             _curl_timeout = NULL;
+          }
+
+        FD_ZERO(&_current_fd_set);
+        if (_url_con_list)
+        {
+           Ecore_Con_Url *con_url;
+           EINA_LIST_FREE(_url_con_list, con_url) ecore_con_url_free(con_url);
+           eina_list_free(_url_con_list);
+           _url_con_list = NULL;
+        }
+        if (_fd_hd_list)
+        {
+           Ecore_Fd_Handler *fd_handler;
+           EINA_LIST_FREE(_fd_hd_list, fd_handler) ecore_main_fd_handler_del(fd_handler);
+           eina_list_free(_fd_hd_list);
+           _fd_hd_list = NULL;
+        }
 
-   curl_global_cleanup();
+        if (_curlm)
+          {
+             curl_multi_cleanup(_curlm);
+             _curlm = NULL;
+          }
+        curl_global_cleanup();
+   }
+   return _init_count;
 #endif
    return 1;
 }
@@ -237,7 +219,6 @@ ecore_con_url_new(const char *url)
    if (!url_con)
      return NULL;
 
-   url_con->fd = -1;
    url_con->write_fd = -1;
 
    url_con->curl_easy = curl_easy_init();
@@ -344,20 +325,9 @@ ecore_con_url_free(Ecore_Con_Url *url_co
      }
 
    ECORE_MAGIC_SET(url_con, ECORE_MAGIC_NONE);
-   if(url_con->fd != -1)
-     {
-        FD_CLR(url_con->fd, &_current_fd_set);
-        if (url_con->fd_handler)
-          ecore_main_fd_handler_del(url_con->fd_handler);
-
-        url_con->fd = -1;
-        url_con->fd_handler = NULL;
-     }
 
    if (url_con->curl_easy)
      {
-        // FIXME: For an unknown reason, progress continue to arrive after destruction
-        // this prevent any further call to the callback.
         curl_easy_setopt(url_con->curl_easy, CURLOPT_PROGRESSFUNCTION, NULL);
         curl_easy_setopt(url_con->curl_easy, CURLOPT_NOPROGRESS, EINA_TRUE);
 
@@ -365,10 +335,11 @@ ecore_con_url_free(Ecore_Con_Url *url_co
           {
              url_con->active = EINA_FALSE;
 
+             // FIXME: Cannot remove fd_handler because curl do not provider fd itself.
+             // For this reason, ecore_main_fd_handler_del() sometimes give warnnings.
+             // But it can be ignored because fd can be already closed by libcurl.
              ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
-             if (ret != CURLM_OK)
-                ERR("curl_multi_remove_handle failed: %s",
-                    curl_multi_strerror(ret));
+             if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
           }
 
         curl_easy_cleanup(url_con->curl_easy);
@@ -697,7 +668,9 @@ _ecore_con_url_send(Ecore_Con_Url *url_c
           }
         else curl_easy_setopt(url_con->curl_easy, CURLOPT_POSTFIELDSIZE, 0);
         if (mode == MODE_POST)
-          curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
+          {
+             curl_easy_setopt(url_con->curl_easy, CURLOPT_POST, 1);
+          }
      }
 
    switch (url_con->time_condition)
@@ -1154,54 +1127,6 @@ ecore_con_url_ssl_ca_set(Ecore_Con_Url *
  */
 
 #ifdef HAVE_CURL
-static int
-_ecore_con_url_suspend_fd_handler(void)
-{
-   Eina_List *l;
-   Ecore_Con_Url *url_con;
-   int deleted = 0;
-
-   if (!_url_con_list)
-     return 0;
-
-   EINA_LIST_FOREACH(_url_con_list, l, url_con)
-     {
-        if (url_con->active && url_con->fd_handler)
-          {
-             ecore_main_fd_handler_del(url_con->fd_handler);
-             url_con->fd_handler = NULL;
-             deleted++;
-          }
-     }
-
-   return deleted;
-}
-
-static int
-_ecore_con_url_restart_fd_handler(void)
-{
-   Eina_List *l;
-   Ecore_Con_Url *url_con;
-   int activated = 0;
-
-   if (!_url_con_list)
-     return 0;
-
-   EINA_LIST_FOREACH(_url_con_list, l, url_con)
-     {
-        if (!url_con->fd_handler && url_con->fd != -1)
-          {
-             url_con->fd_handler =
-               ecore_main_fd_handler_add(url_con->fd, url_con->flags,
-                                         _ecore_con_url_fd_handler,
-                                         NULL, NULL, NULL);
-             activated++;
-          }
-     }
-
-   return activated;
-}
-
 static size_t
 _ecore_con_url_data_cb(void  *buffer,
                        size_t size,
@@ -1266,22 +1191,6 @@ _ecore_con_url_data_cb(void  *buffer,
    return real_size;
 }
 
-#define ECORE_CON_URL_TRANSMISSION(Transmit, Event, Url_con, Total, Now)   \
-  {                                                                        \
-     Ecore_Con_Event_Url_Progress *e;                                      \
-     if ((Total != 0) || (Now != 0))                                       \
-       {                                                                   \
-          e = calloc(1, sizeof(Ecore_Con_Event_Url_Progress));             \
-          if (e)                                                           \
-            {                                                              \
-               e->url_con = url_con;                                       \
-               e->total = Total;                                           \
-               e->now = Now;                                               \
-               ecore_event_add(Event, e, _ecore_con_event_url_free, NULL); \
-            }                                                              \
-       }                                                                   \
-  }
-
 static size_t
 _ecore_con_url_header_cb(void  *ptr,
                          size_t size,
@@ -1358,208 +1267,190 @@ _ecore_con_url_read_cb(void  *ptr,
    return retcode;
 }
 
-static Eina_Bool
-_ecore_con_url_perform(Ecore_Con_Url *url_con)
+static void
+_ecore_con_url_info_read(void)
 {
-   fd_set read_set, write_set, exc_set;
-   int fd_max, fd;
-   int flags, still_running;
-   int completed_immediately = 0;
-   CURLMcode ret;
-
-   _url_con_list = eina_list_append(_url_con_list, url_con);
+   CURLMsg *curlmsg;
+   int n_remaining;
 
-   url_con->active = EINA_TRUE;
-   curl_multi_add_handle(_curlm, url_con->curl_easy);
-   curl_multi_perform(_curlm, &still_running);
-   
-   completed_immediately = _ecore_con_url_process_completed_jobs(url_con);
-
-   if (!completed_immediately)
-     {
-        if (url_con->fd_handler)
-          ecore_main_fd_handler_del(url_con->fd_handler);
-
-        url_con->fd_handler = NULL;
-
-        /* url_con still active -- set up an fd_handler */
-        FD_ZERO(&read_set);
-        FD_ZERO(&write_set);
-        FD_ZERO(&exc_set);
-
-        /* Stupid curl, why can't I get the fd to the current added job? */
-        ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set,
-                               &fd_max);
-        if (ret != CURLM_OK)
+   while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
+     {
+        if (curlmsg->msg == CURLMSG_DONE)
           {
-             ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
-             return EINA_FALSE;
-          }
+             Eina_List *l;
+             Ecore_Con_Url *url_con;
 
-        for (fd = 0; fd <= fd_max; fd++)
-          {
-             if (!FD_ISSET(fd, &_current_fd_set))
+             EINA_LIST_FOREACH(_url_con_list, l, url_con)
                {
-                  flags = 0;
-                  if (FD_ISSET(fd, &read_set))
-                    flags |= ECORE_FD_READ;
-
-                  if (FD_ISSET(fd, &write_set))
-                    flags |= ECORE_FD_WRITE;
+                  if (curlmsg->easy_handle == url_con->curl_easy)
+                    {
+                       CURLMcode ret;
+                       Ecore_Con_Event_Url_Complete *e;
 
-                  if (FD_ISSET(fd, &exc_set))
-                    flags |= ECORE_FD_ERROR;
+                       e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
+                       if (e)
+                         {
+                            e->url_con = url_con;
+                            e->status = 0;
+                            if (curlmsg->data.result == CURLE_OK)
+                              {
+                                 long status; /* curl API uses long, not int */
+                                 status = 0;
+                                 curl_easy_getinfo(curlmsg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
+                                 e->status = status;
+                              }
+                            _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
+                         }
 
-                  if (flags)
-                    {
-                       long ms = 0;
+                       ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
+                       if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
 
-                       ret = curl_multi_timeout(_curlm, &ms);
-                       if (ret != CURLM_OK)
-                         ERR("curl_multi_timeout failed: %s",
-                             curl_multi_strerror(ret));
-
-                       if (ms == 0)
-                         ms = 1000;
-
-                       FD_SET(fd, &_current_fd_set);
-                       url_con->fd = fd;
-                       url_con->flags = flags;
-                       url_con->fd_handler =
-                         ecore_main_fd_handler_add(fd, flags,
-                                                   _ecore_con_url_fd_handler,
-                                                   NULL, NULL, NULL);
+                       _url_con_list = eina_list_remove(_url_con_list, url_con);
                        break;
                     }
                }
           }
-        if (!url_con->fd_handler)
-          {
-             /* Failed to set up an fd_handler */
-              ecore_timer_freeze(_curl_timeout);
-
-              ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
-              if (ret != CURLM_OK)
-                ERR("curl_multi_remove_handle failed: %s",
-                    curl_multi_strerror(ret));
-
-              url_con->active = EINA_FALSE;
-              url_con->fd = -1;
-              return EINA_FALSE;
-          }
-
-        ecore_timer_thaw(_curl_timeout);
      }
-
-   return EINA_TRUE;
 }
 
-static Eina_Bool
-_ecore_con_url_idler_handler(void *data)
+static void
+_ecore_con_url_curl_clear(void)
 {
-   int done, still_running;
-
-   done = (curl_multi_perform(_curlm, &still_running) != CURLM_CALL_MULTI_PERFORM);
+   Eina_List *l;
+   Ecore_Con_Url *url_con;
 
-   _ecore_con_url_process_completed_jobs(NULL);
+   FD_ZERO(&_current_fd_set);
 
-   if (done)
+   if (_fd_hd_list)
      {
-        _ecore_con_url_restart_fd_handler();
-        _fd_idler_handler = NULL;
+        Ecore_Fd_Handler *fd_handler;
+        EINA_LIST_FREE(_fd_hd_list, fd_handler)
+          {
+             int fd = ecore_main_fd_handler_fd_get(fd_handler);
+             FD_CLR(fd, &_current_fd_set);
+             ecore_main_fd_handler_del(fd_handler);
+          }
+        eina_list_free(_fd_hd_list);
+        _fd_hd_list = NULL;
+     }
 
-        if (!_url_con_list)
-          ecore_timer_freeze(_curl_timeout);
+   EINA_LIST_FOREACH(_url_con_list, l, url_con)
+     {
+        CURLMcode ret;
+        Ecore_Con_Event_Url_Complete *e;
 
-        return data ==
-               (void *)0xACE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
+        e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
+        if (e)
+          {
+             e->url_con = url_con;
+             e->status = 0;
+             _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
+          }
+        ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
+        if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
+        _url_con_list = eina_list_remove(_url_con_list, url_con);
      }
-
-   return ECORE_CALLBACK_RENEW;
+     eina_list_free(_url_con_list);
+     _url_con_list = NULL;
 }
 
 static Eina_Bool
-_ecore_con_url_fd_handler(void *data                   __UNUSED__,
-                          Ecore_Fd_Handler *fd_handler __UNUSED__)
+_ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__)
 {
-   _ecore_con_url_suspend_fd_handler();
-
-   if (!_fd_idler_handler)
-     _fd_idler_handler = ecore_idler_add(
-         _ecore_con_url_idler_handler, NULL);
-
+   ecore_timer_thaw(_curl_timeout);
    return ECORE_CALLBACK_RENEW;
 }
 
-static int
-_ecore_con_url_process_completed_jobs(Ecore_Con_Url *url_con_to_match)
+static void
+_ecore_con_url_fdset(void)
 {
-   Eina_List *l;
-   Ecore_Con_Url *url_con;
-   Ecore_Con_Event_Url_Complete *e;
-   CURLMsg *curlmsg;
    CURLMcode ret;
-   int n_remaining;
-   int job_matched = 0;
+   fd_set read_set, write_set, exc_set;
+   int fd, fd_max;
+   Ecore_Fd_Handler *fd_handler;
 
-   /* Loop jobs and check if any are done */
-   while ((curlmsg = curl_multi_info_read(_curlm, &n_remaining)))
+   FD_ZERO(&read_set);
+   FD_ZERO(&write_set);
+   FD_ZERO(&exc_set);
+
+   ret = curl_multi_fdset(_curlm, &read_set, &write_set, &exc_set, &fd_max);
+   if (ret != CURLM_OK)
      {
-        if (curlmsg->msg != CURLMSG_DONE)
-          continue;
+        ERR("curl_multi_fdset failed: %s", curl_multi_strerror(ret));
+        return;
+     }
 
-        /* find the job which is done */
-        EINA_LIST_FOREACH(_url_con_list, l, url_con)
+   for (fd = 0; fd <= fd_max; fd++)
+     {
+        int flags = 0;
+        if (FD_ISSET(fd, &read_set))  flags |= ECORE_FD_READ;
+        if (FD_ISSET(fd, &write_set)) flags |= ECORE_FD_WRITE;
+        if (FD_ISSET(fd, &exc_set))   flags |= ECORE_FD_ERROR;
+        if (flags)
           {
-             if (curlmsg->easy_handle == url_con->curl_easy)
+             if (!FD_ISSET(fd, &_current_fd_set))
                {
-                  if (url_con_to_match &&
-                      (url_con == url_con_to_match))
-                    job_matched = 1;
-
-                  if(url_con->fd != -1)
-                    {
-                       FD_CLR(url_con->fd, &_current_fd_set);
-                       if (url_con->fd_handler)
-                         ecore_main_fd_handler_del(
-                           url_con->fd_handler);
+                  FD_SET(fd, &_current_fd_set);
+                  fd_handler = ecore_main_fd_handler_add(fd, flags, _ecore_con_url_fd_handler, NULL, NULL, NULL);
+                  if (fd_handler) _fd_hd_list = eina_list_append(_fd_hd_list, fd_handler);
+                  ecore_timer_freeze(_curl_timeout);
+               }
+          }
+     }
+}
 
-                       url_con->fd = -1;
-                       url_con->fd_handler = NULL;
-                    }
+static Eina_Bool
+_ecore_con_url_idler_handler(void *data __UNUSED__)
+{
+   int still_running;
+   CURLMcode ret;
 
-                  _url_con_list = eina_list_remove(_url_con_list, url_con);
-                  url_con->active = EINA_FALSE;
-                  e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
-                  if (e)
-                    {
-                       e->url_con = url_con;
-                       e->status = 0;
-                       if (curlmsg->data.result == CURLE_OK)
-                         {
-                            long status; /* curl API uses long, not int */
+   ret = curl_multi_perform(_curlm, &still_running);
+   if (ret != CURLM_OK)
+     {
+        ERR("curl_multi_perform() failed: %s", curl_multi_strerror(ret));
+        _ecore_con_url_curl_clear();
+        ecore_timer_freeze(_curl_timeout);
+        return ECORE_CALLBACK_RENEW;
+     }
+   if (ret == CURLM_CALL_MULTI_PERFORM)
+     {
+        DBG("Call multiperform again");
+        return ECORE_CALLBACK_RENEW;
+     }
 
-                            status = 0;
-                            curl_easy_getinfo(curlmsg->easy_handle,
-                                              CURLINFO_RESPONSE_CODE,
-                                              &status);
-                            e->status = status;
-                         }
+   _ecore_con_url_info_read();
+   if (still_running)
+     {
+        DBG("multiperform is still_running");
+        _ecore_con_url_fdset();
+     }
+   else
+     {
+        DBG("multiperform ended");
+        _ecore_con_url_curl_clear();
+        ecore_timer_freeze(_curl_timeout);
+     }
 
-                       _url_complete_push_event(ECORE_CON_EVENT_URL_COMPLETE, e);
-                    }
+   return ECORE_CALLBACK_RENEW;
+}
 
-                  ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
-                  if (ret != CURLM_OK)
-                    ERR("curl_multi_remove_handle failed: %s",
-                        curl_multi_strerror(ret));
+static Eina_Bool
+_ecore_con_url_perform(Ecore_Con_Url *url_con)
+{
+   CURLMcode ret;
 
-                  break;
-               }
-          }
+   ret = curl_multi_add_handle(_curlm, url_con->curl_easy);
+   if (ret != CURLM_OK)
+     {
+        ERR("curl_multi_add_handle() failed: %s", curl_multi_strerror(ret));
+        return EINA_FALSE;
      }
 
-   return job_matched;
+   _url_con_list = eina_list_append(_url_con_list, url_con);
+   ecore_timer_thaw(_curl_timeout);
+
+   return EINA_TRUE;
 }
 
 static void
------------------------------------------------------------------------------
RSA(R) Conference 2012
Save $700 by Nov 18
Register now
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to