| New file |
| | |
| | | #ifndef _QTL_DATABASE_POOL_H_ |
| | | #define _QTL_DATABASE_POOL_H_ |
| | | |
| | | #include <memory> |
| | | #include <vector> |
| | | #include <atomic> |
| | | #include <thread> |
| | | #include <mutex> |
| | | #include <chrono> |
| | | #include <algorithm> |
| | | |
| | | namespace qtl |
| | | { |
| | | |
| | | template<typename Database> |
| | | class database_pool |
| | | { |
| | | public: |
| | | typedef Database value_type; |
| | | typedef std::shared_ptr<Database> pointer; |
| | | |
| | | database_pool() |
| | | : m_trying_connection(false), m_stop_thread(false) |
| | | { |
| | | } |
| | | |
| | | virtual ~database_pool() |
| | | { |
| | | if(m_background_thread.joinable()) |
| | | { |
| | | m_stop_thread=true; |
| | | m_background_thread.join(); |
| | | } |
| | | clear(); |
| | | } |
| | | |
| | | pointer get() |
| | | { |
| | | Database* db=popup(); |
| | | if(db==NULL && m_trying_connection==false) |
| | | db=create_database(); |
| | | return pointer(db, [this](Database* db) { |
| | | recovery(db); |
| | | }); |
| | | } |
| | | |
| | | bool test_alive() |
| | | { |
| | | if(m_databases.empty()) |
| | | return false; |
| | | std::unique_lock<std::mutex> lock(m_pool_mutex); |
| | | auto it=m_databases.begin(); |
| | | while(it!=m_databases.end()) |
| | | { |
| | | Database* db=*it; |
| | | if(!db->is_alive()) |
| | | { |
| | | delete db; |
| | | it=m_databases.erase(it); |
| | | } |
| | | else |
| | | { |
| | | ++it; |
| | | } |
| | | } |
| | | if(m_databases.empty()) |
| | | { |
| | | lock.unlock(); |
| | | try_connect(); |
| | | return false; |
| | | } |
| | | else return true; |
| | | } |
| | | |
| | | private: |
| | | std::vector<Database*> m_databases; |
| | | std::mutex m_pool_mutex; |
| | | std::atomic<bool> m_trying_connection; |
| | | std::thread m_background_thread; |
| | | bool m_stop_thread; |
| | | |
| | | virtual bool open_database(Database& db)=0; |
| | | void recovery(Database* db) |
| | | { |
| | | if(db==NULL) return; |
| | | if(db->is_alive()) |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_pool_mutex); |
| | | m_databases.push_back(db); |
| | | } |
| | | else |
| | | { |
| | | delete db; |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_pool_mutex); |
| | | clear(); |
| | | } |
| | | try_connect(); |
| | | } |
| | | } |
| | | |
| | | Database* create_database() |
| | | { |
| | | Database* db=new Database; |
| | | if(open_database(*db)) |
| | | return db; |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_pool_mutex); |
| | | clear(); |
| | | } |
| | | try_connect(); |
| | | return NULL; |
| | | } |
| | | |
| | | Database* popup() |
| | | { |
| | | Database* db=NULL; |
| | | std::lock_guard<std::mutex> lock(m_pool_mutex); |
| | | if(!m_databases.empty()) |
| | | { |
| | | db=m_databases.back(); |
| | | m_databases.pop_back(); |
| | | } |
| | | return db; |
| | | } |
| | | |
| | | void try_connect() |
| | | { |
| | | if(m_trying_connection) |
| | | return; |
| | | |
| | | m_trying_connection=true; |
| | | try |
| | | { |
| | | m_background_thread=std::thread(&database_pool<Database>::background_connect, this); |
| | | } |
| | | catch (std::system_error&) |
| | | { |
| | | m_trying_connection=false; |
| | | } |
| | | } |
| | | |
| | | void background_connect() |
| | | { |
| | | Database* db=NULL; |
| | | int interval=1; |
| | | while(db==NULL && m_stop_thread==false) |
| | | { |
| | | db=create_database(); |
| | | if(db==NULL) |
| | | { |
| | | std::this_thread::sleep_for(std::chrono::seconds(interval)); |
| | | if(interval<60) interval<<=1; |
| | | } |
| | | } |
| | | if(db) |
| | | { |
| | | recovery(db); |
| | | } |
| | | m_background_thread.detach(); |
| | | m_trying_connection=false; |
| | | } |
| | | |
| | | void clear() |
| | | { |
| | | std::for_each(m_databases.begin(), m_databases.end(), std::default_delete<Database>()); |
| | | m_databases.clear(); |
| | | } |
| | | }; |
| | | |
| | | } |
| | | |
| | | #endif //_QTL_DATABASE_POOL_H_ |