Files
SingularityViewer/indra/llmessage/aicurlthread.h
Aleric Inglewood 69ca6cd5b2 WIP: Make curl thread code robust and flexible.
Conflicts:

	indra/llmessage/llcurl.cpp
	indra/llmessage/llcurl.h
	indra/newview/app_settings/settings.xml
	indra/newview/llappviewer.cpp
	indra/newview/llmeshrepository.cpp

Resolved:

	indra/llmessage/llcurl.cpp:

	  Basically removed (not used anyway)

	indra/llmessage/llcurl.h:

	  Basically removed (just includes aiculr.h now)

	indra/newview/app_settings/settings.xml:

	  CurlUseMultipleThreads was remvoved.
	  CurlMaximumNumberOfHandles and CurlRequestTimeOut
	  are still in there, but unused at the moment.

	indra/newview/llappviewer.cpp:

	  CurlMaximumNumberOfHandles and CurlRequestTimeOut
	  are unused at the moment.

	indra/newview/llmeshrepository.cpp:

	  Lock mSignal always (is unlocked inside wait()).
	  Use mSignal lock to see if we are waiting; remove mWaiting.
	  Return false from the MeshFetch functions iff we have to retry
	  a HTTP fetch. Catch the error exception thrown by getByteRange
	  instead of using it's return value (always returns true
	  anyway).
2012-06-28 01:30:46 +02:00

186 lines
6.8 KiB
C++

/**
* @file aicurlthread.h
* @brief Thread safe wrapper for libcurl.
*
* Copyright (c) 2012, Aleric Inglewood.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 28/04/2012
* Initial version, written by Aleric Inglewood @ SL
*/
#ifndef AICURLTHREAD_H
#define AICURLTHREAD_H
#include "aicurl.h"
#include <vector>
#undef AICurlPrivate
namespace AICurlPrivate {
namespace curlthread {
// For ordering a std::set with AICurlEasyRequest objects.
struct AICurlEasyRequestCompare {
bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); }
};
//-----------------------------------------------------------------------------
// PollSet
int const empty = 0x1;
int const complete = 0x2;
enum refresh_t {
not_complete_not_empty = 0,
complete_not_empty = complete,
empty_and_complete = complete|empty
};
class PollSet
{
public:
PollSet(void);
// Add/remove a filedescriptor to/from mFileDescriptors.
void add(curl_socket_t s);
void remove(curl_socket_t s);
// Copy mFileDescriptors to an internal fd_set that is returned by access().
// Returns if all fds could be copied (complete) and/or if the resulting fd_set is empty.
refresh_t refresh(void);
// Return a pointer to the underlaying fd_set.
fd_set* access(void) { return &mFdSet; }
// Return the largest fd set in mFdSet by refresh.
int get_max_fd(void) const { return mMaxFdSet; }
// Return true if a filedescriptor is set in mFileDescriptors (used for debugging).
bool contains(curl_socket_t s) const;
// Return true if a filedescriptor is set in mFdSet.
bool is_set(curl_socket_t s) const;
// Clear filedescriptor in mFdSet.
void clr(curl_socket_t fd);
// Iterate over all file descriptors that were set by refresh and are still set in mFdSet.
void reset(void); // Reset the iterator.
int get(void) const; // Return next filedescriptor, or -1 when there are no more.
// Only valid if reset() was called after the last call to refresh().
void next(void); // Advance to next filedescriptor.
private:
curl_socket_t* mFileDescriptors;
size_t mSize; // Size of mFileDescriptors array.
int mNrFds; // The number of filedescriptors in the array.
int mMaxFd; // The largest filedescriptor in the array, or -1 when it is empty.
int mNext; // The index of the first file descriptor to start copying, the next call to refresh().
fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh().
int mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or -1 when it was empty.
std::vector<curl_socket_t> mCopiedFileDescriptors; // File descriptors copied by refresh to mFdSet.
std::vector<curl_socket_t>::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable.
};
//-----------------------------------------------------------------------------
// MultiHandle
// This class adds member functions that will only be called from the AICurlThread thread.
// This class guarantees that all added easy handles will be removed from the multi handle
// before the multi handle is cleaned up, as is required by libcurl.
class MultiHandle : public CurlMultiHandle
{
public:
MultiHandle(void);
~MultiHandle();
// Add/remove an easy handle to/from a multi session.
CURLMcode add_easy_request(AICurlEasyRequest const& easy_request);
CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request);
// Reads/writes available data from a particular socket (non-blocking).
CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask);
// Set data to association with an internal socket.
CURLMcode assign(curl_socket_t sockfd, void* sockptr);
// Read multi stack informationals.
CURLMsg const* info_read(int* msgs_in_queue) const;
private:
typedef std::set<AICurlEasyRequest, AICurlEasyRequestCompare> addedEasyRequests_type;
addedEasyRequests_type mAddedEasyRequests;
bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count().
int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with.
int mRunningHandles; // The last value returned by curl_multi_socket_action.
long mTimeOut; // The last time out in ms as set by the call back CURLMOPT_TIMERFUNCTION.
private:
static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp);
static int timer_callback(CURLM* multi, long timeout_ms, void* userp);
public:
// Returns the number of active easy handles as reported by the last call to curl_multi_socket_action.
int getRunningHandles(void) const { return mRunningHandles; }
// Returns how long to wait for socket action before calling socket_action(CURL_SOCKET_TIMEOUT, 0), in ms.
int getTimeOut(void) const { return mTimeOut; }
// This is called before sleeping, after calling (one or more times) socket_action.
void check_run_count(void);
public:
//-----------------------------------------------------------------------------
// Curl socket administration:
PollSet mReadPollSet;
PollSet mWritePollSet;
};
} // namespace curlthread
} // namespace AICurlPrivate
// Thread safe, noncopyable curl multi handle.
// This class wraps MultiHandle for thread-safety.
// AIThreadSafeSingleThreadDC cannot be copied, but that is OK as we don't need that (or want that);
// this class provides a thread-local singleton (exactly one instance per thread), and because it
// can't be copied, that guarantees that the CURLM* handle is never used concurrently, which is
// not allowed by libcurl.
class AICurlMultiHandle : public AIThreadSafeSingleThreadDC<AICurlPrivate::curlthread::MultiHandle>, public LLThreadLocalDataMember {
public:
static AICurlMultiHandle& getInstance(void);
private:
// Use getInstance().
AICurlMultiHandle(void) { }
};
typedef AISTAccessConst<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_rat;
typedef AISTAccess<AICurlPrivate::curlthread::MultiHandle> AICurlMultiHandle_wat;
#define AICurlPrivate DONTUSE_AICurlPrivate
#endif