diff --git a/src/README.md b/src/README.md index b97b10f..95036b0 100644 --- a/src/README.md +++ b/src/README.md @@ -31,3 +31,7 @@ A high-performance spin mutex and locker. ## Protocol/TelentProtocol.h/cpp An implementation of Telnet protocol. + +## Utility/ThreadLocal.h/cpp + +A high-performance thread local storage. diff --git a/src/Utility/ThreadLocal.cpp b/src/Utility/ThreadLocal.cpp new file mode 100644 index 0000000..e4529ef --- /dev/null +++ b/src/Utility/ThreadLocal.cpp @@ -0,0 +1,31 @@ + /* + * Copyright 2020, WindTerm. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ThreadLocal.h" + +#include + +void ThreadLocalWatcher::stopWatchCurrentThread() { + QThread::currentThread()->disconnect(this); +} + +void ThreadLocalWatcher::watchCurrentThread() { + boost::thread::id id = boost::this_thread::get_id(); + + connect(QThread::currentThread(), &QThread::finished, this, [this, id]() { + clear(id); + }); +} \ No newline at end of file diff --git a/src/Utility/ThreadLocal.h b/src/Utility/ThreadLocal.h new file mode 100644 index 0000000..7ffbd3a --- /dev/null +++ b/src/Utility/ThreadLocal.h @@ -0,0 +1,205 @@ + /* + * Copyright 2020, WindTerm. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef THREADLOCAL_H +#define THREADLOCAL_H + +#include + +#include +#include +#include + +#include "Spin.h" + +#define STATIC_LOCAL_COUNT 6 + +class ThreadLocalWatcher : public QObject { + Q_OBJECT + +public: + ThreadLocalWatcher() = default; + +protected: + void stopWatchCurrentThread(); + void watchCurrentThread(); + virtual void clear(const boost::thread::id &id) = 0; + +private: + Q_DISABLE_COPY(ThreadLocalWatcher) +}; + +template +class ThreadLocal : public ThreadLocalWatcher { +public: + ThreadLocal() + : m_mainThreadId(boost::this_thread::get_id()) + { + for (int i = 0; i < STATIC_LOCAL_COUNT; i++) { + m_staticLocals[i] = nullptr; + } + } + + ~ThreadLocal() { + clearAll(); + } + + void clear(const boost::thread::id &id) { + std::lock_guard lock(m_mutex); + + if (removeLocal(id)) { + stopWatchCurrentThread(); + } + } + + void clearAll() { + std::lock_guard lock(m_mutex); + + for (int i = 0; i < STATIC_LOCAL_COUNT; i++) { + if (Local *local = m_staticLocals[i]) { + delete local; + m_staticLocals[i] = nullptr; + } + } + + if (m_dynamicLocals) { + for (Local *local : *m_dynamicLocals) { + delete local; + } + m_dynamicLocals.reset(nullptr); + } + } + + T *get() { + boost::thread::id id = boost::this_thread::get_id(); + + for (const Local *local : m_staticLocals) { + if (local && local->id == id) { + return local->ptr; + } + } + + if (m_dynamicLocals) { + std::lock_guard lock(m_mutex); + + for (const Local *local : *m_dynamicLocals) { + if (local && local->id == id) { + return local->ptr; + } + } + } + return nullptr; + } + + boost::thread::id mainThreadId() const { + return m_mainThreadId; + } + + void reset(T *t) { + std::lock_guard lock(m_mutex); + boost::thread::id id = boost::this_thread::get_id(); + + bool removed = removeLocal(id); + bool added = addLocal(id, t); + + if (id != m_mainThreadId) { + if (removed) { + if (added == false) { + stopWatchCurrentThread(); + } + } else { + if (added) { + watchCurrentThread(); + } + } + } + } + +private: + bool addLocal(const boost::thread::id &id, T *t) { + if (t == nullptr) { + return false; + } + + for (int i = 0; i < STATIC_LOCAL_COUNT; i++) { + if (m_staticLocals[i] == nullptr) { + m_staticLocals[i] = new Local(id, t); + + return true; + } + } + + if (m_dynamicLocals.get() == nullptr) { + m_dynamicLocals = std::make_unique(); + } + m_dynamicLocals->push_back(new Local(id, t)); + return true; + } + + bool removeLocal(const boost::thread::id &id) { + for (int i = 0; i < STATIC_LOCAL_COUNT; i++) { + Local *local = m_staticLocals[i]; + + if (local && local->id == id) { + delete local; + m_staticLocals[i] = nullptr; + + return true; + } + } + + if (m_dynamicLocals) { + for (auto it = m_dynamicLocals->begin(); it != m_dynamicLocals->end(); ++it) { + Local *local = *it; + + if (local && local->id == id) { + delete local; + m_dynamicLocals->erase(it); + + return true; + } + } + } + return false; + } + +private: + Q_DISABLE_COPY(ThreadLocal) + + struct Local { + boost::thread::id id; + T *ptr; + + Local(boost::thread::id id_, T *ptr_) + : id(id_) + , ptr(ptr_) + {} + + ~Local() { + delete ptr; + ptr = nullptr; + } + }; + typedef std::vector LocalVector; + std::unique_ptr m_dynamicLocals; + + Local *m_staticLocals[STATIC_LOCAL_COUNT]; + + boost::thread::id m_mainThreadId; + SpinMutex m_mutex; +}; + +#endif // THREADLOCAL_H \ No newline at end of file