2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
23 #include "DllLibCurl.h"
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/TimeUtils.h"
30 using namespace XCURL;
32 /* okey this is damn ugly. our dll loader doesn't allow for postload, preunload functions */
33 static long g_curlReferences = 0;
35 static unsigned int g_curlTimeout = 0;
38 bool DllLibCurlGlobal::Load()
40 CSingleLock lock(m_critSection);
41 if(g_curlReferences > 0)
47 /* we handle this ourself */
48 DllDynamic::EnableDelayedUnload(false);
49 if (!DllDynamic::Load())
52 if (global_init(CURL_GLOBAL_ALL))
55 CLog::Log(LOGERROR, "Error initializing libcurl");
59 /* check idle will clean up the last one */
65 void DllLibCurlGlobal::Unload()
67 CSingleLock lock(m_critSection);
68 if (--g_curlReferences == 0)
79 /* CheckIdle will clear this one up */
81 if(g_curlReferences == 1)
82 g_curlTimeout = XbmcThreads::SystemClockMillis();
86 void DllLibCurlGlobal::CheckIdle()
88 /* avoid locking section here, to avoid stalling gfx thread on loads*/
89 if(g_curlReferences == 0)
92 CSingleLock lock(m_critSection);
93 /* 20 seconds idle time before closing handle */
94 const unsigned int idletime = 30000;
96 VEC_CURLSESSIONS::iterator it = m_sessions.begin();
97 while(it != m_sessions.end())
99 if( !it->m_busy && (XbmcThreads::SystemClockMillis() - it->m_idletimestamp) > idletime )
101 CLog::Log(LOGINFO, "%s - Closing session to %s://%s (easy=%p, multi=%p)\n", __FUNCTION__, it->m_protocol.c_str(), it->m_hostname.c_str(), (void*)it->m_easy, (void*)it->m_multi);
103 // It's important to clean up multi *before* cleaning up easy, because the multi cleanup
104 // code accesses stuff in the easy's structure.
106 multi_cleanup(it->m_multi);
108 easy_cleanup(it->m_easy);
112 it = m_sessions.erase(it);
118 /* check if we should unload the dll */
119 #if(0) // we never unload libcurl, since libssl can break when python unloads then
120 if(g_curlReferences == 1 && XbmcThreads::SystemClockMillis() - g_curlTimeout > idletime)
125 void DllLibCurlGlobal::easy_aquire(const char *protocol, const char *hostname, CURL_HANDLE** easy_handle, CURLM** multi_handle)
127 assert(easy_handle != NULL);
129 CSingleLock lock(m_critSection);
131 VEC_CURLSESSIONS::iterator it;
132 for(it = m_sessions.begin(); it != m_sessions.end(); it++)
136 /* allow reuse of requester is trying to connect to same host */
137 /* curl will take care of any differences in username/password */
138 if( it->m_protocol.compare(protocol) == 0 && it->m_hostname.compare(hostname) == 0)
144 it->m_easy = easy_init();
146 *easy_handle = it->m_easy;
152 it->m_multi = multi_init();
154 *multi_handle = it->m_multi;
162 SSession session = {};
163 session.m_busy = true;
164 session.m_protocol = protocol;
165 session.m_hostname = hostname;
167 /* count up global interface counter */
172 session.m_easy = easy_init();
173 *easy_handle = session.m_easy;
178 session.m_multi = multi_init();
179 *multi_handle = session.m_multi;
182 m_sessions.push_back(session);
185 CLog::Log(LOGINFO, "%s - Created session to %s://%s\n", __FUNCTION__, protocol, hostname);
191 void DllLibCurlGlobal::easy_release(CURL_HANDLE** easy_handle, CURLM** multi_handle)
193 CSingleLock lock(m_critSection);
195 CURL_HANDLE* easy = NULL;
206 multi = *multi_handle;
207 *multi_handle = NULL;
210 VEC_CURLSESSIONS::iterator it;
211 for(it = m_sessions.begin(); it != m_sessions.end(); it++)
213 if( it->m_easy == easy && (multi == NULL || it->m_multi == multi) )
215 /* reset session so next caller doesn't reuse options, only connections */
216 /* will reset verbose too so it won't print that it closed connections on cleanup*/
219 it->m_idletimestamp = XbmcThreads::SystemClockMillis();
225 CURL_HANDLE* DllLibCurlGlobal::easy_duphandle(CURL_HANDLE* easy_handle)
227 CSingleLock lock(m_critSection);
229 VEC_CURLSESSIONS::iterator it;
230 for(it = m_sessions.begin(); it != m_sessions.end(); it++)
232 if( it->m_easy == easy_handle )
234 SSession session = *it;
235 session.m_easy = DllLibCurl::easy_duphandle(easy_handle);
237 m_sessions.push_back(session);
238 return session.m_easy;
241 return DllLibCurl::easy_duphandle(easy_handle);
244 void DllLibCurlGlobal::easy_duplicate(CURL_HANDLE* easy, CURLM* multi, CURL_HANDLE** easy_out, CURLM** multi_out)
246 CSingleLock lock(m_critSection);
249 *easy_out = DllLibCurl::easy_duphandle(easy);
251 if(multi_out && multi)
252 *multi_out = DllLibCurl::multi_init();
254 VEC_CURLSESSIONS::iterator it;
255 for(it = m_sessions.begin(); it != m_sessions.end(); it++)
257 if( it->m_easy == easy )
259 SSession session = *it;
261 session.m_easy = *easy_out;
263 session.m_easy = NULL;
265 if(multi_out && multi)
266 session.m_multi = *multi_out;
268 session.m_multi = NULL;
271 m_sessions.push_back(session);