diff options
-rw-r--r-- | indra/llcorehttp/_httplibcurl.cpp | 97 | ||||
-rw-r--r-- | indra/llcorehttp/_httplibcurl.h | 16 | ||||
-rw-r--r-- | indra/llcorehttp/_httpoperation.cpp | 36 | ||||
-rw-r--r-- | indra/llcorehttp/_httpoperation.h | 25 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicy.cpp | 15 | ||||
-rw-r--r-- | indra/llcorehttp/_httppolicy.h | 15 | ||||
-rw-r--r-- | indra/llcorehttp/_httprequestqueue.h | 6 | ||||
-rw-r--r-- | indra/llcorehttp/_httpservice.cpp | 75 | ||||
-rw-r--r-- | indra/llcorehttp/_httpservice.h | 11 | ||||
-rw-r--r-- | indra/llcorehttp/_thread.h | 20 | ||||
-rw-r--r-- | indra/llcorehttp/httprequest.cpp | 43 | ||||
-rw-r--r-- | indra/llcorehttp/httprequest.h | 9 | ||||
-rw-r--r-- | indra/llcorehttp/tests/test_httprequest.hpp | 154 |
13 files changed, 447 insertions, 75 deletions
diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 45e16d420e..bc8b3cc9be 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -47,12 +47,19 @@ HttpLibcurl::HttpLibcurl(HttpService * service) HttpLibcurl::~HttpLibcurl() { - // *FIXME: need to cancel requests in this class, not in op class. + shutdown(); + + mService = NULL; +} + + +void HttpLibcurl::shutdown() +{ while (! mActiveOps.empty()) { active_set_t::iterator item(mActiveOps.begin()); - (*item)->cancel(); + cancelRequest(*item); (*item)->release(); mActiveOps.erase(item); } @@ -63,8 +70,6 @@ HttpLibcurl::~HttpLibcurl() { if (mMultiHandles[policy_class]) { - // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); mMultiHandles[policy_class] = 0; } @@ -73,20 +78,12 @@ HttpLibcurl::~HttpLibcurl() delete [] mMultiHandles; mMultiHandles = NULL; } - - mService = NULL; -} - -void HttpLibcurl::init() -{} - - -void HttpLibcurl::term() -{} + mPolicyCount = 0; +} -void HttpLibcurl::setPolicyCount(int policy_count) +void HttpLibcurl::start(int policy_count) { llassert_always(policy_count <= POLICY_CLASS_LIMIT); llassert_always(! mMultiHandles); // One-time call only @@ -143,8 +140,9 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } else { - // *FIXME: Issue a logging event for this. - ; + LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; } msgs_in_queue = 0; } @@ -191,30 +189,61 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. Caller is expected to +// remove to op from the active list and release the op *after* +// calling this method. It must be called first to deliver the +// op to the reply queue with refcount intact. +void HttpLibcurl::cancelRequest(HttpOpRequest * op) +{ + // Deactivate request + op->mCurlActive = false; + + // Detach from multi and recycle handle + curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + curl_easy_cleanup(op->mCurlHandle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " + << static_cast<HttpHandle>(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Cancel op and deliver for notification + op->cancel(); +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer if (handle != op->mCurlHandle || ! op->mCurlActive) { - // *FIXME: This is a sanity check that needs validation/termination. - ; + LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast<HttpHandle>(handle) + << LL_ENDL; + return false; } active_set_t::iterator it(mActiveOps.find(op)); if (mActiveOps.end() == it) { - // *FIXME: Fatal condition. This must be here. - ; - } - else - { - mActiveOps.erase(it); + LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast<HttpHandle>(handle) + << LL_ENDL; + return false; } // Deactivate request + mActiveOps.erase(it); op->mCurlActive = false; // Set final status of request if it hasn't failed by other mechanisms yet @@ -258,9 +287,21 @@ int HttpLibcurl::getActiveCount() const } -int HttpLibcurl::getActiveCountInClass(int /* policy_class */) const +int HttpLibcurl::getActiveCountInClass(int policy_class) const { - return getActiveCount(); + int count(0); + + for (active_set_t::const_iterator iter(mActiveOps.begin()); + mActiveOps.end() != iter; + ++iter) + { + if ((*iter)->mReqPolicy == policy_class) + { + ++count; + } + } + + return count; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 5e1dd1bfbf..69f7bb2b6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -65,9 +65,6 @@ private: void operator=(const HttpLibcurl &); // Not defined public: - static void init(); - static void term(); - /// Give cycles to libcurl to run active requests. Completed /// operations (successful or failed) will be retried or handed /// over to the reply queue as final responses. @@ -79,16 +76,23 @@ public: void addOp(HttpOpRequest * op); /// One-time call to set the number of policy classes to be - /// serviced and to create the resources for each. - void setPolicyCount(int policy_count); + /// serviced and to create the resources for each. Value + /// must agree with HttpPolicy::setPolicies() call. + void start(int policy_count); + + void shutdown(); int getActiveCount() const; int getActiveCountInClass(int policy_class) const; - + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + + /// Invoked to cancel an active request, mainly during shutdown + /// and destroy. + void cancelRequest(HttpOpRequest * op); protected: typedef std::set<HttpOpRequest *> active_set_t; diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 0d9553434e..b35ab79d65 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -208,4 +208,40 @@ void HttpOpNull::stageFromRequest(HttpService * service) } +// ================================== +// HttpOpSpin +// ================================== + + +HttpOpSpin::HttpOpSpin(int mode) + : HttpOperation(), + mMode(mode) +{} + + +HttpOpSpin::~HttpOpSpin() +{} + + +void HttpOpSpin::stageFromRequest(HttpService * service) +{ + if (0 == mMode) + { + // Spin forever + while (true) + { + ms_sleep(100); + } + } + else + { + this->addRef(); + if (! HttpRequestQueue::instanceOf()->addOp(this)) + { + this->release(); + } + } +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 5823c08c7b..717a9b0d72 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -170,6 +170,31 @@ public: }; // end class HttpOpNull +/// HttpOpSpin is a test-only request that puts the worker +/// thread into a cpu spin. Used for unit tests and cleanup +/// evaluation. You do not want to use this. +class HttpOpSpin : public HttpOperation +{ +public: + // 0 does a hard spin in the operation + // 1 does a soft spin continuously requeuing itself + HttpOpSpin(int mode); + +protected: + virtual ~HttpOpSpin(); + +private: + HttpOpSpin(const HttpOpSpin &); // Not defined + void operator=(const HttpOpSpin &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + int mMode; +}; // end class HttpOpSpin + + } // end namespace LLCore #endif // _LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1dae20add6..93e295537c 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -76,6 +76,14 @@ HttpPolicy::HttpPolicy(HttpService * service) HttpPolicy::~HttpPolicy() { + shutdown(); + + mService = NULL; +} + + +void HttpPolicy::shutdown() +{ for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); @@ -100,12 +108,12 @@ HttpPolicy::~HttpPolicy() } delete [] mState; mState = NULL; - mService = NULL; + mActiveClasses = 0; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, - const std::vector<HttpPolicyClass> & classes) +void HttpPolicy::start(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes) { llassert_always(! mState); @@ -244,6 +252,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior return false; } + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index c93279bc83..90bb3b571d 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -60,6 +60,17 @@ private: void operator=(const HttpPolicy &); // Not defined public: + /// Cancel all ready and retry requests sending them to + /// their notification queues. Release state resources + /// making further request handling impossible. + void shutdown(); + + /// Deliver policy definitions and enable handling of + /// requests. One-time call invoked before starting + /// the worker thread. + void start(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes); + /// Give the policy layer some cycles to scan the ready /// queue promoting higher-priority requests to active /// as permited. @@ -98,10 +109,6 @@ public: return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global, - const std::vector<HttpPolicyClass> & classes); - - // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 6e8f00c4da..e11fd17c90 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -73,11 +73,15 @@ public: public: typedef std::vector<HttpOperation *> OpContainer; - /// Insert an object at the back of the reply queue. + /// Insert an object at the back of the request queue. /// /// Caller must provide one refcount to the queue which takes /// possession of the count. /// + /// @return Standard status. On failure, caller + /// must dispose of the operation with + /// an explicit release() call. + /// /// Threading: callable by any thread. HttpStatus addOp(HttpOperation * op); diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 9c5c7bf9b4..afbab2ab71 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,35 +53,43 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + // Create the default policy class HttpPolicyClass pol_class; pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); - mPolicyClasses.push_back(pol_class); } HttpService::~HttpService() { + mExitRequested = true; + if (RUNNING == sState) + { + // Trying to kill the service object with a running thread + // is a bit tricky. + if (mThread) + { + mThread->cancel(); + + if (! mThread->timedJoin(2000)) + { + // Failed to join, expect problems ahead... + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; + } + } + } + if (mRequestQueue) { mRequestQueue->release(); mRequestQueue = NULL; } - if (mPolicy) - { - // *TODO: need a finalization here - ; - } - - if (mTransport) - { - // *TODO: need a finalization here - delete mTransport; - mTransport = NULL; - } + delete mTransport; + mTransport = NULL; delete mPolicy; mPolicy = NULL; @@ -110,9 +118,22 @@ void HttpService::init(HttpRequestQueue * queue) void HttpService::term() { - llassert_always(RUNNING != sState); if (sInstance) { + if (RUNNING == sState) + { + // Unclean termination. Thread appears to be running. We'll + // try to give the worker thread a chance to cancel using the + // exit flag... + sInstance->mExitRequested = true; + + // And a little sleep + ms_sleep(1000); + + // Dtor will make some additional efforts and issue any final + // warnings... + } + delete sInstance; sInstance = NULL; } @@ -159,9 +180,9 @@ void HttpService::startThread() mThread->release(); } - // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); - mTransport->setPolicyCount(mPolicyClasses.size()); + // Push current policy definitions, enable policy & transport components + mPolicy->start(mPolicyGlobal, mPolicyClasses); + mTransport->start(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally @@ -174,6 +195,7 @@ void HttpService::stopRequested() mExitRequested = true; } + bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { bool found(false); @@ -191,9 +213,26 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio void HttpService::shutdown() { + // Disallow future enqueue of requests mRequestQueue->stopQueue(); - // *FIXME: Run down everything.... + // Cancel requests alread on the request queue + HttpRequestQueue::OpContainer ops; + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + op->cancel(); + op->release(); + } + + // Shutdown transport canceling requests, freeing resources + mTransport->shutdown(); + + // And now policy + mPolicy->shutdown(); } diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 43044d97c0..a74235c475 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -134,7 +134,7 @@ public: /// acquires its weaknesses. static bool isStopped(); - /// Threading: callable by application thread *once*. + /// Threading: callable by consumer thread *once*. void startThread(); /// Threading: callable by worker thread. @@ -152,23 +152,28 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// Threading: callable by worker thread. HttpPolicy & getPolicy() { return *mPolicy; } + /// Threading: callable by worker thread. HttpLibcurl & getTransport() { return *mTransport; } + /// Threading: callable by consumer thread. HttpPolicyGlobal & getGlobalOptions() { return mPolicyGlobal; } + /// Threading: callable by consumer thread. HttpRequest::policy_t createPolicyClass(); + /// Threading: callable by consumer thread. HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) { llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); @@ -187,9 +192,9 @@ protected: static volatile EState sState; HttpRequestQueue * mRequestQueue; volatile bool mExitRequested; - - // === calling-thread-only data === LLCoreInt::HttpThread * mThread; + + // === consumer-thread-only data === HttpPolicyGlobal mPolicyGlobal; std::vector<HttpPolicyClass> mPolicyClasses; diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 46a333a749..4cf35055e9 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -27,9 +27,11 @@ #ifndef LLCOREINT_THREAD_H_ #define LLCOREINT_THREAD_H_ +#include "linden_common.h" + #include <boost/thread.hpp> #include <boost/function.hpp> - +#include <boost/date_time/posix_time/posix_time_types.hpp> #include "_refcounted.h" @@ -91,11 +93,27 @@ public: mThread->join(); } + inline bool timedJoin(S32 millis) + { + return mThread->timed_join(boost::posix_time::milliseconds(millis)); + } + inline bool joinable() const { return mThread->joinable(); } + // A very hostile method to force a thread to quit + inline void cancel() + { + boost::thread::native_handle_type thread(mThread->native_handle()); +#if LL_WINDOWS + TerminateThread(thread, 0); +#else + pthread_cancel(thread); +#endif + } + private: boost::function<void(HttpThread *)> mThreadFunc; boost::thread * mThread; diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index a525d8f9ea..3a55a849b9 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -370,11 +370,13 @@ HttpStatus HttpRequest::createService() { HttpStatus status; - llassert_always(! has_inited); - HttpRequestQueue::init(); - HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); - HttpService::init(rq); - has_inited = true; + if (! has_inited) + { + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + } return status; } @@ -384,10 +386,12 @@ HttpStatus HttpRequest::destroyService() { HttpStatus status; - llassert_always(has_inited); - HttpService::term(); - HttpRequestQueue::term(); - has_inited = false; + if (has_inited) + { + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + } return status; } @@ -423,6 +427,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) return handle; } + +HttpHandle HttpRequest::requestSpin(int mode) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSpin * op = new HttpOpSpin(mode); + op->setReplyPath(mReplyQueue, NULL); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + // ==================================== // Dynamic Policy Methods // ==================================== diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 24fff24b83..134a61b618 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -409,6 +409,15 @@ public: /// HttpHandle requestStopThread(HttpHandler * handler); + /// Queue a Spin request. + /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for + /// test purposes. + /// + /// @param mode 0 for hard spin, 1 for soft spin + /// @return Standard handle return cases. + /// + HttpHandle requestSpin(int mode); + /// @} /// @name DynamicPolicyMethods diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index b09db6db28..ed049aa09c 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1230,10 +1230,158 @@ void HttpRequestTestObjectType::test<11>() } } +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + // *NB: This test must be last. The sleeping webserver // won't respond for a long time. template <> template <> -void HttpRequestTestObjectType::test<12>() +void HttpRequestTestObjectType::test<14>() { ScopedCurlInit ready; @@ -1352,7 +1500,9 @@ void HttpRequestTestObjectType::test<12>() throw; } } - +// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a +// sleeping service that interferes with other HTTP tests. Keep it +// last until that little HTTP server can get some attention... } // end namespace tut |