So I'm pretty new to CPP and i was trying to implement a resource pool (SQLITE connections), for a small project that I'm developing.
The problem is that I have a list(vector) with objects created at the beginning of the program, that have the given connection and its availability (atomic_bool). If my program requests a connection from this pool, the following function gets executed:
gardener_db &connection_pool_inner::get_connection() {
bool expected = false;
for(auto & pair : pool) {
std::atomic_bool &ref = pair->get_is_busy_ref();
if (ref.compare_exchange_strong(expected, true)) {
return pair->get_conn();
}//if false it means that its busy
}
std::lock_guard<std::mutex> guard(vector_mutex);//increment size
std::atomic_bool t = true;
pool.emplace_back(std::make_shared<pool_elem>());
pool.back()->get_is_busy_ref().store(t);// because we are giving the resource to the caller
return pool.back()->get_conn();
}
So I made a simple test to see if my vector is resizing:
constexpr unsigned int CONNECTION_POOL_START_SIZE = 20;
TEST(testConnPool, regrow) {
auto saturation = std::vector<connection_handler>();
ASSERT_EQ(saturation.size(), 0);
for(int i = 0; i < CONNECTION_POOL_START_SIZE; i++) {
saturation.emplace_back();
}
auto ptr = connection_pool_inner::instance();
auto size = ptr->get_pool().size();
//it should be full at this point
ASSERT_EQ(size, CONNECTION_POOL_START_SIZE);
}
the problem is that I get 22 as my size as opposed to 20 which is what I would expect.
I was able to narrow down the problem to the compare_exchage_strong() but, it was my understanding that the "strong" variant wouldn't fail. So I debugged it, and its always the third element of my vector that gets skipped, (even when working on a single thread).
I already test it the same thing on a different computer(and architecture) but the same problem still occurs so I'm guessing the problem is the logic.
Any ideas about what's going on?
MRE
#include <mutex>
#include <atomic>
#include <iostream>
#include <vector>
#include <memory>
class foo {
std::atomic_bool inner;
public:
explicit foo(): inner(false){};
std::atomic_bool &get(){return inner;}
};
std::mutex vector_mutex;
std::vector<std::shared_ptr<foo>> resources;
void get_resource() {
bool expected = false;
for(auto & rsc : resources) {
if (rsc->get().compare_exchange_strong(expected, true)) {
return;
}
}
std::lock_guard<std::mutex> guard(vector_mutex);//increment size
resources.emplace_back(std::make_shared<foo>());
}
int main() {
std::vector<std::shared_ptr<foo>> *local = &resources;
for(int i = 0; i < 20; i++) {
resources.emplace_back(std::make_shared<foo>());
}
for(int i = 0; i < 20; i++) {
get_resource();
}
std::cout << resources.size();
}