I am currently having a problem with too many database connections. Therefore, I am trying to implement a database connection timeout. In the code below the changing of value of the shared resource will be replaced with connecting and disconnecting to the database. I am new to programming with threads and I kindly wonder if I have made any logical errors or if there are any better approaches to this problem? There is no built-in timeout in OCCI which I use.
#include <thread>
#include <chrono>
#include <iostream>
#include <mutex>
#include <atomic>
namespace chrono = std::chrono;
class scoped_thread{
std::thread t;
public:
explicit scoped_thread(std::thread t_): t(std::move(t_)){
if ( !t.joinable()) throw std::logic_error("No thread");
}
~scoped_thread(){
t.join();
std::cout << "Thread " << std::this_thread::get_id() << " joined.\n";
}
scoped_thread(scoped_thread&)= delete;
scoped_thread& operator=(scoped_thread const &)= delete;
};
template<typename T>
class LockWrapper {
T* t;
std::mutex mut;
public:
class Proxy {
T* t;
std::unique_lock<std::mutex> lock;
Proxy(LockWrapper& wr) : t(wr.t), lock(wr.mut) { }
friend LockWrapper;
public:
T& operator*() { return *t; }
T* operator->() { return t; }
};
LockWrapper(T* t) : t(t) {}
Proxy aquire() { return {*this}; }
};
using clock_type = chrono::system_clock;
using shared_deadline = std::atomic<clock_type::time_point>;
constexpr auto tlim=100;
template<typename T> void timeout(T& shared_resource,const shared_deadline& deadline) {
while(clock_type::now()<deadline.load())
{
constexpr auto sleeping_time=1;
std::cout << "Sleeping for another " << 1 << " seconds\n";
std::this_thread::sleep_until(clock_type::now()+chrono::seconds(1));
}
*shared_resource.aquire()=0; // will replace by database disconnect
std::cout << "Setting resource to " << (*shared_resource.aquire()) << " after timeout occurred in thread " << std::this_thread::get_id() << ".\n";
}
int main()
{
int int_value=1;
LockWrapper<int> shared_resource(&int_value);
constexpr auto timeout_length=10;
auto get_new_deadline=[timeout_length](){return clock_type::now()+chrono::seconds(timeout_length);};
shared_deadline deadline(get_new_deadline());
auto th = std::make_unique<scoped_thread>(std::thread(timeout<LockWrapper<int>>, std::ref(shared_resource),std::cref(deadline)));
constexpr int sleeping_time=20;
for(auto t=0;t<tlim;t+=sleeping_time) // This loop simulates repeated database access
{
std::cout << "Slept for " << t << " seconds. Sleeping for another " << sleeping_time << " seconds\n";
std::this_thread::sleep_for(chrono::seconds(sleeping_time));
if((*shared_resource.aquire())==0)
{
*shared_resource.aquire()=1; // will replace by database connect
std::cout << "Setting resource to " << (*shared_resource.aquire()) << "\n";
deadline=get_new_deadline();
th = std::make_unique<scoped_thread>(std::thread(timeout<LockWrapper<int>>, std::ref(shared_resource),std::cref(deadline)));
}
else
{
deadline=get_new_deadline();
}
}
}