Thread storage (#265)

This commit is contained in:
Daniel Mensinger 2019-10-02 16:29:25 +02:00 committed by An Tao
parent f9d7f589a2
commit ffb8a9d0e4
5 changed files with 158 additions and 3 deletions

View File

@ -374,6 +374,7 @@ set(DROGON_HEADERS
lib/inc/drogon/HttpTypes.h
lib/inc/drogon/HttpViewData.h
lib/inc/drogon/IntranetIpFilter.h
lib/inc/drogon/IOThreadStorage.h
lib/inc/drogon/LocalHostFilter.h
lib/inc/drogon/MultiPart.h
lib/inc/drogon/NotFound.h

View File

@ -0,0 +1,154 @@
/**
*
* IOThreadStorage.h
* Daniel Mensinger
*
* Copyright 2019, Daniel Mensinger. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include <memory>
#include <vector>
#include <limits>
#include <functional>
#include <drogon/HttpAppFramework.h>
namespace drogon
{
/**
* @brief Utility class for thread storage handling
*
* Thread storage allows the efficient handling of reusable data without thread
* synchronisation. For instance, such a thread storage would be useful to store
* database connections.
*
* Example usage:
*
* @code
* struct MyThreadData {
* int threadLocal = 42;
* std::string something = "foo";
* };
*
* class MyController : public HttpController<MyController> {
* public:
* METHOD_LIST_BEGIN
* ADD_METHOD_TO(MyController::endpoint, "/some/path", Get);
* METHOD_LIST_END
*
* void login(const HttpRequestPtr &req,
* std::function<void (const HttpResponsePtr &)> &&callback) {
* assert(_storage->threadLocal == 42);
*
* // handle the request
* }
*
* private:
* IOThreadStorage<MyThreadData> _storage;
* };
* @endcode
*/
template <class C,
bool ConstructorInitialize = true,
template <class> class StoragePtrType = std::shared_ptr>
class IOThreadStorage
{
public:
static const bool isConstructorInitialized = ConstructorInitialize;
using StoragePtr = StoragePtrType<C>;
using CreatorCallback = std::function<StoragePtr(size_t idx)>;
template <typename U = C,
typename = typename std::enable_if<
std::is_default_constructible<U>::value &&
std::is_same<StoragePtrType<U>, std::shared_ptr<U>>::value &&
ConstructorInitialize>::type>
IOThreadStorage()
: IOThreadStorage([](size_t) { return std::make_shared<C>(); })
{
}
template <
typename U = C,
typename = typename std::enable_if<
!ConstructorInitialize ||
!std::is_same<StoragePtrType<U>, std::shared_ptr<U>>::value>::type,
typename = U>
IOThreadStorage() : IOThreadStorage([](size_t) { return nullptr; })
{
}
IOThreadStorage(const CreatorCallback &creator)
{
size_t numThreads = app().getThreadNum();
assert(numThreads > 0 &&
numThreads != std::numeric_limits<size_t>::max());
_storage.resize(numThreads);
for (size_t i = 0; i < numThreads; ++i)
{
_storage[i] = creator(i);
}
}
/**
* @brief Get the thread storage asociate with the current thread
*
* This function may only be called in a request handler
*/
inline StoragePtr &getThreadData()
{
size_t idx = app().getCurrentThreadIndex();
assert(idx < _storage.size());
return _storage[idx];
}
/**
* @brief Sets the thread data for the current thread
*
* This function may only be called in a request handler
*/
inline void setThreadData(const StoragePtr &newData)
{
size_t idx = app().getCurrentThreadIndex();
assert(idx < _storage.size());
_storage[idx] = newData;
}
inline void setThreadData(StoragePtr &&newData)
{
size_t idx = app().getCurrentThreadIndex();
assert(idx < _storage.size());
_storage[idx] = std::move(newData);
}
inline C *operator->()
{
size_t idx = app().getCurrentThreadIndex();
assert(idx < _storage.size());
return _storage[idx].get();
}
inline StoragePtr &operator*()
{
return getThreadData();
}
inline explicit operator bool() const
{
return (bool)getThreadData();
}
private:
std::vector<StoragePtr> _storage;
};
} // namespace drogon

View File

@ -29,6 +29,7 @@
#include <drogon/plugins/Plugin.h>
#include <drogon/Cookie.h>
#include <drogon/Session.h>
#include <drogon/IOThreadStorage.h>
#include <drogon/UploadFile.h>
#include <drogon/orm/DbClient.h>

View File

@ -398,7 +398,7 @@ void HttpAppFrameworkImpl::run()
_threadNum,
_syncAdvices);
assert(ioLoops.size() == _threadNum);
for (size_t i=0; i < _threadNum; ++i)
for (size_t i = 0; i < _threadNum; ++i)
{
ioLoops[i]->setIndex(i);
}

View File

@ -230,8 +230,7 @@ void HttpSimpleControllersRouter::doControllerHandler(
else
{
loop->queueInLoop([loop, resp, &ctrlBinderPtr]() {
ctrlBinderPtr->_responseCache[loop->index()] =
resp;
ctrlBinderPtr->_responseCache[loop->index()] = resp;
ctrlBinderPtr->_hasCachedResponse = true;
});
}