From d54c3cfade903d5cb80905387a0d8b8f37c5a3f5 Mon Sep 17 00:00:00 2001 From: Leon Hartley Date: Fri, 1 Jun 2018 19:57:30 +0100 Subject: [PATCH 1/4] Remove boost dependency, use std::lock_guard and other standard types --- ConnectionPool.h | 215 +++++++++++++++++++++++----------------------- DummyConnection.h | 50 +++++------ MySQLConnection.h | 78 ++++++++--------- 3 files changed, 167 insertions(+), 176 deletions(-) diff --git a/ConnectionPool.h b/ConnectionPool.h index a000753..daa035a 100644 --- a/ConnectionPool.h +++ b/ConnectionPool.h @@ -26,173 +26,172 @@ // Define your custom logging function by overriding this #define #ifndef _DEBUG - #define _DEBUG(x) +#define _DEBUG(x) #endif - #include #include -#include -#include #include #include -using namespace std; -using boost::shared_ptr; +#include +#include namespace active911 { - struct ConnectionUnavailable : std::exception { + struct ConnectionUnavailable : std::exception { + + char const *what() const throw() { - char const* what() const throw() { + return "Unable to allocate connection"; + }; + }; - return "Unable to allocate connection"; - }; - }; + class Connection { - class Connection { + public: + Connection() {}; - public: - Connection(){}; - virtual ~Connection(){}; + virtual ~Connection() {}; - }; + }; - class ConnectionFactory { + class ConnectionFactory { - public: - virtual shared_ptr create()=0; - }; + public: + virtual std::shared_ptr create()=0; + }; - struct ConnectionPoolStats { + struct ConnectionPoolStats { - size_t pool_size; - size_t borrowed_size; + size_t pool_size; + size_t borrowed_size; - }; + }; - template - class ConnectionPool { + template + class ConnectionPool { - public: + public: - ConnectionPoolStats get_stats() { + ConnectionPoolStats get_stats() { - // Lock - boost::mutex::scoped_lock lock(this->io_mutex); + // Lock + std::lock_guard lock(this->io_mutex); - // Get stats - ConnectionPoolStats stats; - stats.pool_size=this->pool.size(); - stats.borrowed_size=this->borrowed.size(); + // Get stats + ConnectionPoolStats stats; + stats.pool_size = this->pool.size(); + stats.borrowed_size = this->borrowed.size(); - return stats; - }; + return stats; + }; - ConnectionPool(size_t pool_size, shared_ptr factory){ + ConnectionPool(size_t pool_size, std::shared_ptr factory) { - // Setup - this->pool_size=pool_size; - this->factory=factory; + // Setup + this->pool_size = pool_size; + this->factory = factory; - // Fill the pool - while(this->pool.size() < this->pool_size){ + // Fill the pool + while (this->pool.size() < this->pool_size) { - this->pool.push_back(this->factory->create()); - } + this->pool.push_back(this->factory->create()); + } - }; + }; - ~ConnectionPool() { + ~ConnectionPool() { - }; + }; - /** - * Borrow - * - * Borrow a connection for temporary use - * - * When done, either (a) call unborrow() to return it, or (b) (if it's bad) just let it go out of scope. This will cause it to automatically be replaced. - * @retval a shared_ptr to the connection object - */ - shared_ptr borrow(){ + /** + * Borrow + * + * Borrow a connection for temporary use + * + * When done, either (a) call unborrow() to return it, or (b) (if it's bad) just let it go out of scope. This will cause it to automatically be replaced. + * @retval a shared_ptr to the connection object + */ + std::shared_ptr borrow() { - // Lock - boost::mutex::scoped_lock lock(this->io_mutex); + // Lock + std::lock_guard lock(this->io_mutex); - // Check for a free connection - if(this->pool.size()==0){ + // Check for a free connection + if (this->pool.size() == 0) { - // Are there any crashed connections listed as "borrowed"? - for(std::set >::iterator it=this->borrowed.begin(); it!=this->borrowed.end(); ++it){ + // Are there any crashed connections listed as "borrowed"? + for (std::set>::iterator it = this->borrowed.begin(); + it != this->borrowed.end(); ++it) { - if((*it).unique()) { + if ((*it).unique()) { - // This connection has been abandoned! Destroy it and create a new connection - try { + // This connection has been abandoned! Destroy it and create a new connection + try { - // If we are able to create a new connection, return it - _DEBUG("Creating new connection to replace discarded connection"); - shared_ptr conn=this->factory->create(); - this->borrowed.erase(it); - this->borrowed.insert(conn); - return boost::static_pointer_cast(conn); + // If we are able to create a new connection, return it + _DEBUG("Creating new connection to replace discarded connection"); + std::shared_ptr conn = this->factory->create(); - } catch(std::exception& e) { + this->borrowed.erase(it); + this->borrowed.insert(conn); - // Error creating a replacement connection - throw ConnectionUnavailable(); - } - } - } + return std::static_pointer_cast(conn); - // Nothing available - throw ConnectionUnavailable(); - } + } catch (std::exception &e) { - // Take one off the front - shared_ptrconn=this->pool.front(); - this->pool.pop_front(); + // Error creating a replacement connection + throw ConnectionUnavailable(); + } + } + } - // Add it to the borrowed list - this->borrowed.insert(conn); + // Nothing available + throw ConnectionUnavailable(); + } - return boost::static_pointer_cast(conn); - }; + // Take one off the front + std::shared_ptr conn = this->pool.front(); + this->pool.pop_front(); - /** - * Unborrow a connection - * - * Only call this if you are returning a working connection. If the connection was bad, just let it go out of scope (so the connection manager can replace it). - * @param the connection - */ - void unborrow(shared_ptr conn) { + // Add it to the borrowed list + this->borrowed.insert(conn); - // Lock - boost::mutex::scoped_lock lock(this->io_mutex); + return std::static_pointer_cast(conn); + }; - // Push onto the pool - this->pool.push_back(boost::static_pointer_cast(conn)); + /** + * Unborrow a connection + * + * Only call this if you are returning a working connection. If the connection was bad, just let it go out of scope (so the connection manager can replace it). + * @param the connection + */ + void unborrow(std::shared_ptr conn) { - // Unborrow - this->borrowed.erase(conn); + // Lock + std::lock_guard lock(this->io_mutex); - }; + // Push onto the pool + this->pool.push_back(std::static_pointer_cast(conn)); - protected: - shared_ptr factory; - size_t pool_size; - deque > pool; - set > borrowed; - boost::mutex io_mutex; + // Unborrow + this->borrowed.erase(conn); - }; + }; + protected: + std::shared_ptr factory; + size_t pool_size; + std::deque> pool; + std::set> borrowed; + std::mutex io_mutex; + }; } \ No newline at end of file diff --git a/DummyConnection.h b/DummyConnection.h index a9de03e..d9fbc9d 100644 --- a/DummyConnection.h +++ b/DummyConnection.h @@ -14,57 +14,53 @@ */ #include "ConnectionPool.h" -using boost::shared_ptr; namespace active911 { - class DummyConnection : public Connection { + class DummyConnection : public Connection { - public: + public: - DummyConnection() { + DummyConnection() { - _DEBUG("Dummy connection created"); + _DEBUG("Dummy connection created"); - }; + }; - ~DummyConnection() { + ~DummyConnection() { - // Destroy the connection + // Destroy the connection - _DEBUG("Dummy connection destroyed"); + _DEBUG("Dummy connection destroyed"); - }; + }; - // shared_ptr to some kind of actual connection object would go here + // shared_ptr to some kind of actual connection object would go here - }; + }; - class DummyConnectionFactory : public ConnectionFactory { + class DummyConnectionFactory : public ConnectionFactory { - public: - DummyConnectionFactory() { + public: + DummyConnectionFactory() { - }; + }; - // Any exceptions thrown here should be caught elsewhere - shared_ptr create() { + // Any exceptions thrown here should be caught elsewhere + std::shared_ptr create() { - // Create the connection - shared_ptrconn(new DummyConnection()); - - // Perform some kind of ->connect operation here - - return boost::static_pointer_cast(conn); - }; - - }; + // Create the connection + std::shared_ptr conn(new DummyConnection()); + // Perform some kind of ->connect operation here + return std::static_pointer_cast(conn); + }; + }; } \ No newline at end of file diff --git a/MySQLConnection.h b/MySQLConnection.h index 82656d3..2e05ddc 100644 --- a/MySQLConnection.h +++ b/MySQLConnection.h @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "ConnectionPool.h" +#include "ConnectionPool.h" #include #include "mysql_connection.h" #include @@ -21,69 +21,65 @@ #include #include -using boost::shared_ptr; - namespace active911 { - class MySQLConnection : public Connection { - - public: - + class MySQLConnection : public Connection { - ~MySQLConnection() { + public: - if(this->sql_connection) { - _DEBUG("MYSQL Destruct"); + ~MySQLConnection() { - this->sql_connection->close(); - this->sql_connection.reset(); // Release and destruct - - } + if (this->sql_connection) { - }; + _DEBUG("MYSQL Destruct"); - boost::shared_ptr sql_connection; - int a; - }; + this->sql_connection->close(); + this->sql_connection.reset(); // Release and destruct + } - class MySQLConnectionFactory : public ConnectionFactory { + }; - public: - MySQLConnectionFactory(std::string server, std::string username, std::string password) { + std::shared_ptr sql_connection; + int a; + }; - this->server=server; - this->username=username; - this->password=password; - }; + class MySQLConnectionFactory : public ConnectionFactory { - // Any exceptions thrown here should be caught elsewhere - shared_ptr create() { + public: + MySQLConnectionFactory(std::string server, std::string username, std::string password) { - // Get the driver - sql::Driver *driver; - driver=get_driver_instance(); + this->server = server; + this->username = username; + this->password = password; - // Create the connection - shared_ptrconn(new MySQLConnection()); + }; - // Connect - conn->sql_connection=boost::shared_ptr(driver->connect(this->server,this->username,this->password)); + // Any exceptions thrown here should be caught elsewhere + std::shared_ptr create() { - return boost::static_pointer_cast(conn); - }; + // Get the driver + sql::Driver *driver; + driver = get_driver_instance(); - private: - string server; - string username; - string password; - }; + // Create the connection + std::shared_ptr conn(new MySQLConnection()); + // Connect + conn->sql_connection = std::shared_ptr( + driver->connect(this->server, this->username, this->password)); + return std::static_pointer_cast(conn); + }; + private: + std::string server; + std::string username; + std::string password; + }; } \ No newline at end of file From 4a454d30b2758c9f4d085d69b308c329c312199d Mon Sep 17 00:00:00 2001 From: Leon Hartley Date: Fri, 1 Jun 2018 20:01:31 +0100 Subject: [PATCH 2/4] Update example --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 13511ab..0bf9fc4 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,16 @@ We needed a safe, efficient way to concurrently access MySQL without connection ### Example ```cpp #include -#include +#include #include "MySQLConnection.h" // Create a pool of 5 MySQL connections -shared_ptrmysql_connection_factory(new MySQLConnectionFactory("mysql_server","mysql_username","mysql_password")); -shared_ptr >mysql_pool(new ConnectionPool(5, mysql_connection_factory)); +std::shared_ptrmysql_connection_factory(new MySQLConnectionFactory("mysql_server","mysql_username","mysql_password")); +std::shared_ptr >mysql_pool(new ConnectionPool(5, mysql_connection_factory)); // Get a connection and do something with it (throws exception if pool is completely used) -shared_ptr conn=mysql_pool->borrow(); +std::shared_ptr conn=mysql_pool->borrow(); // conn->sql_connection->do_whatever(); /* If our code dies here, the connection will be closed and replaced with a new one! :) */ @@ -40,11 +40,11 @@ mysql_pool->unborrow(conn); ``` ### Design notes -Connections are stored as a std::deque of boost::shared_ptr so we can pop from the front and push from back; this makes sure all connections get cycled through as fast as possible (so we don't accidentally hang onto any dead ones for a long time). +Connections are stored as a std::deque of std::shared_ptr so we can pop from the front and push from back; this makes sure all connections get cycled through as fast as possible (so we don't accidentally hang onto any dead ones for a long time). We managed to get all of this WITHOUT a separate curator thread. Calling borrow() should only block very briefly while we access the pool deque, etc. If we have to replace a dead connection with a new one, borrow() will additionally block while the new connection is set up. If we are still unable to serve a live connection, borrow() will throw. -The use of boost::shared_ptr for tracking connections yeilds some nice side effects. If you have a problem with a connection, just let the shared_ptr fall out of scope and the connection will automagically be closed (immediately) and then replaced with a brand new connection (later, when we need one). In this way, it promotes 'safe' handling of connections without the need to manually ping() (or whatever) each connection periodically. +The use of std::shared_ptr for tracking connections yeilds some nice side effects. If you have a problem with a connection, just let the shared_ptr fall out of scope and the connection will automagically be closed (immediately) and then replaced with a brand new connection (later, when we need one). In this way, it promotes 'safe' handling of connections without the need to manually ping() (or whatever) each connection periodically. Debugging may be enabled by using the ```#define _DEBUG(x)``` macro: From d6ce19822ffaa520926e8467d1d3a2bee07d6844 Mon Sep 17 00:00:00 2001 From: Leon Hartley Date: Fri, 1 Jun 2018 20:01:45 +0100 Subject: [PATCH 3/4] No more Boost --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0bf9fc4..01ac5e4 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,6 @@ Debugging may be enabled by using the ```#define _DEBUG(x)``` macro: ### Dependencies -- Boost - Connection/C++ (for MySQL implementation) From 9cae92dfe9cbc9add7b4deffa050922df83ae8ac Mon Sep 17 00:00:00 2001 From: Leon Hartley Date: Fri, 1 Jun 2018 20:44:03 +0100 Subject: [PATCH 4/4] Header guards --- ConnectionPool.h | 5 +++-- DummyConnection.h | 2 ++ MySQLConnection.h | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ConnectionPool.h b/ConnectionPool.h index daa035a..f3ea5ab 100644 --- a/ConnectionPool.h +++ b/ConnectionPool.h @@ -19,10 +19,11 @@ * * Connection and ConnectionFactory are virtual classes that should be overridden to their actual type. * - * NOTE: To avoid using templates AND inheritance at the same time in the ConnectionFactory, ConnectionFactory::create must create a derved type - * but return the base class. + * NOTE: To avoid using templates AND inheritance at the same time in the ConnectionFactory, ConnectionFactory::create must create a derved type + * but return the base class. */ +#pragma once // Define your custom logging function by overriding this #define #ifndef _DEBUG diff --git a/DummyConnection.h b/DummyConnection.h index d9fbc9d..e99912c 100644 --- a/DummyConnection.h +++ b/DummyConnection.h @@ -13,6 +13,8 @@ * limitations under the License. */ +#pragma once + #include "ConnectionPool.h" namespace active911 { diff --git a/MySQLConnection.h b/MySQLConnection.h index 2e05ddc..f3939e1 100644 --- a/MySQLConnection.h +++ b/MySQLConnection.h @@ -12,6 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#pragma once + #include "ConnectionPool.h" #include #include "mysql_connection.h"