Skip to content
Snippets Groups Projects
Commit f14b1940 authored by Recolic Keghart's avatar Recolic Keghart
Browse files

fixed passing. TODO: more tests

parent 9fd25a21
No related branches found
No related tags found
No related merge requests found
...@@ -8,6 +8,7 @@ Here is recolic's private library... ...@@ -8,6 +8,7 @@ Here is recolic's private library...
# TODO # TODO
```c++ ```c++
/*
RETEST rlib::noncopyable RETEST rlib::noncopyable
rlib::stdio fd control is still a problem. rlib::stdio fd control is still a problem.
...@@ -17,4 +18,5 @@ rlib::meta::array::to_tuple ...@@ -17,4 +18,5 @@ rlib::meta::array::to_tuple
rlib::meta::string rlib::meta::string
#DONE#rlib::logger #DONE#rlib::logger
``` */
```
\ No newline at end of file
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <tuple> #include <tuple>
#include <functional> #include <functional>
#include <algorithm> #include <algorithm>
#include <atomic>
#ifdef RLIB_SWITCH_USE_MINGW_THREAD_FIX #ifdef RLIB_SWITCH_USE_MINGW_THREAD_FIX
#include <mingw.mutex.h> #include <mingw.mutex.h>
...@@ -17,29 +18,75 @@ ...@@ -17,29 +18,75 @@
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#endif #endif
using size_t = unsigned long;
namespace rlib { namespace rlib {
struct object_pool_policy_fixed {
object_pool_policy_fixed(size_t size) : size(size), used(new std::atomic<size_t>(0)) {}
bool borrow_should_alloc(/*const size_t free_objects, const size_t existing_objects*/) { // TODO: add size argument?
// object_pool::borrow calls this funcion.
// If it returns true, object pool will alloc a new object.
// If it returns false, object pool will not alloc a new object.
// If it returns false, THIS FUNCTION MUST HAVE NO SIDE EFFECT!
// If there's no free object, object pool::borrow blocks.
while(true) {
auto used_old = used->load(std::memory_order_acquire);
if(used_old >= size) {
return false;
}
auto used_new = used_old + 1;
if(used->compare_exchange_strong(used_old, used_new, std::memory_order_acq_rel))
break; // success
}
return true;
}
bool release_should_free() {
if(used->load() == 0)
throw std::runtime_error("POLICY detected error: Release object of zero-sized object pool.");
(*used)--;
return false;
}
private:
const size_t size;
std::unique_ptr<std::atomic<size_t> > used;
};
struct object_pool_policy_dynamic_never_free {
object_pool_policy_dynamic_never_free() : fixed(std::numeric_limits<size_t>::max()) {}
bool borrow_should_alloc() {
return fixed.borrow_should_alloc();
}
bool release_should_free() {
return fixed.release_should_free();
}
private:
object_pool_policy_fixed fixed;
};
struct object_pool_policy_dynamic_smart {
};
/* /*
* Multi-threaded object_pool. It will block current thread and wait if * Multi-threaded object_pool. It will block current thread and wait if
* borrow_one() starves, until some other threads release their obj. * borrow_one() starves, until some other threads release their obj.
*/ */
template<typename obj_t, typename... _bound_construct_args_t> template<typename policy_t, typename obj_t, typename... _bound_construct_args_t>
class fixed_object_pool : rlib::nonmovable { class object_pool : rlib::nonmovable {
protected: protected:
using element_t = obj_t; using element_t = obj_t;
using buffer_t = impl::traceable_list<obj_t, bool>; using buffer_t = impl::traceable_list<obj_t, bool>;
using this_type = fixed_object_pool<obj_t, _bound_construct_args_t ...>; using this_type = object_pool<obj_t, _bound_construct_args_t ...>;
public: public:
explicit fixed_object_pool(size_t max_size, _bound_construct_args_t ... _args) object_pool() = delete;
: max_size(max_size), _bound_args(std::forward<_bound_construct_args_t>(_args) ...) explicit object_pool(policy_t &&policy, _bound_construct_args_t ... _args)
: policy(std::forward<policy_t>(policy)), _bound_args(std::forward<_bound_construct_args_t>(_args) ...)
{} {}
void fill_full() { // void fill_full() {
for (size_t cter = 0; cter < max_size; ++cter) { // for (size_t cter = 0; cter < max_size; ++cter) {
new_obj_to_buffer(); // new_obj_to_buffer();
free_list.push_back(&*--buffer.end()); // free_list.push_back(&*--buffer.end());
} // }
} // }
// `new` an object. Return nullptr if pool is full. // `new` an object. Return nullptr if pool is full.
obj_t *try_borrow_one() { obj_t *try_borrow_one() {
...@@ -53,6 +100,7 @@ namespace rlib { ...@@ -53,6 +100,7 @@ namespace rlib {
// Not available. Wait for release_one. // Not available. Wait for release_one.
std::unique_lock<std::mutex> lk(buffer_mutex); std::unique_lock<std::mutex> lk(buffer_mutex);
// wait for a release
borrow_cv.wait(lk, [this]{return this->new_obj_ready;}); borrow_cv.wait(lk, [this]{return this->new_obj_ready;});
result = do_try_borrow_one(); result = do_try_borrow_one();
...@@ -76,12 +124,17 @@ namespace rlib { ...@@ -76,12 +124,17 @@ namespace rlib {
reconstruct_impl(which, std::make_index_sequence<sizeof...(_bound_construct_args_t)>()); reconstruct_impl(which, std::make_index_sequence<sizeof...(_bound_construct_args_t)>());
} }
size_t size() const {
return curr_size;
}
protected: protected:
buffer_t buffer; // list<obj_t obj, bool is_free> buffer_t buffer; // list<obj_t obj, bool is_free>
private: private:
std::tuple<_bound_construct_args_t ...> _bound_args; std::tuple<_bound_construct_args_t ...> _bound_args;
size_t max_size; size_t curr_size = 0;
policy_t policy;
std::list<obj_t *> free_list; std::list<obj_t *> free_list;
std::mutex buffer_mutex; std::mutex buffer_mutex;
std::condition_variable borrow_cv; std::condition_variable borrow_cv;
...@@ -90,7 +143,11 @@ namespace rlib { ...@@ -90,7 +143,11 @@ namespace rlib {
// try_borrow_one without lock. // try_borrow_one without lock.
obj_t *do_try_borrow_one() { obj_t *do_try_borrow_one() {
// Optimize here if is performance bottleneck (lockless list... etc...) // Optimize here if is performance bottleneck (lockless list... etc...)
borrow_again: // NOT THREAD SAFE. USE buffer_mutex.
if(policy.borrow_should_alloc()) {
new_obj_to_buffer();
free_list.push_back(&*--buffer.end());
}
if (free_list.size() > 0) { if (free_list.size() > 0) {
// Some object is free. Just return one. // Some object is free. Just return one.
obj_t *result = *free_list.begin(); obj_t *result = *free_list.begin();
...@@ -99,13 +156,9 @@ namespace rlib { ...@@ -99,13 +156,9 @@ namespace rlib {
typename buffer_t::iterator elem_iter(result); typename buffer_t::iterator elem_iter(result);
elem_iter.get_extra_info() = false; // mark as busy. elem_iter.get_extra_info() = false; // mark as busy.
new_obj_ready = false; new_obj_ready = false;
++curr_size;
return result; return result;
} }
if (buffer.size() < max_size) {
new_obj_to_buffer();
free_list.push_back(&*--buffer.end());
goto borrow_again;
}
return nullptr; return nullptr;
} }
......
...@@ -40,7 +40,7 @@ endif ...@@ -40,7 +40,7 @@ endif
POSTFIX=$(STD)_$(CXX) POSTFIX=$(STD)_$(CXX)
all: string common all: string common pool
common: common:
$(CXX) $(CXXFLAGS) src/common.cc $(CXXFLAGS) -o src/common_$(POSTFIX).out $(CXX) $(CXXFLAGS) src/common.cc $(CXXFLAGS) -o src/common_$(POSTFIX).out
...@@ -50,6 +50,10 @@ string: ...@@ -50,6 +50,10 @@ string:
$(CXX) $(CXXFLAGS) src/string.cc $(CXXFLAGS) -o src/string_$(POSTFIX).out $(CXX) $(CXXFLAGS) src/string.cc $(CXXFLAGS) -o src/string_$(POSTFIX).out
src/string_$(POSTFIX).out src/string_$(POSTFIX).out
pool:
$(CXX) $(CXXFLAGS) src/pool.cc $(CXXFLAGS) -o src/pool_$(POSTFIX).out
src/pool_$(POSTFIX).out
clean: clean:
rm -f src/*.out rm -f src/*.out
......
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <rlib/pool.hpp>
#include <string>
using std::string;
struct pooled_obj_t {
pooled_obj_t(int arg1, string arg2)
: arg1(arg1), arg2(arg2) {}
pooled_obj_t(const pooled_obj_t &another) = delete;
pooled_obj_t() = delete;
int arg1;
string arg2;
};
TEST_CASE("fixed object pool") {
size_t pool_size = 8;
const auto arg1 = 666;
const auto arg2 = string("fuck you");
rlib::object_pool<rlib::object_pool_policy_fixed, pooled_obj_t, int, string>
fixed_pool(rlib::object_pool_policy_fixed(pool_size), arg1, arg2);
auto res = fixed_pool.try_borrow_one();
REQUIRE(res != nullptr);
REQUIRE(res->arg1 == arg1);
REQUIRE(res->arg2 == arg2);
size_t test_rounds = 1024;
for(auto _ = 0; _ < test_rounds; ++_) {
std::list<decltype(res)> objs;
for(auto cter = 0; cter < pool_size - 1; ++cter) {
auto ptr = fixed_pool.try_borrow_one();
REQUIRE(ptr != nullptr);
REQUIRE(ptr->arg2 == arg2);
fixed_pool.reconstruct_one(ptr);
REQUIRE(ptr->arg2 == arg2);
objs.push_back(ptr);
}
REQUIRE(fixed_pool.try_borrow_one() == nullptr);
REQUIRE(fixed_pool.try_borrow_one() == nullptr);
for(auto cter = 0; cter < pool_size - 1; ++cter) {
fixed_pool.release_one(*objs.begin());
objs.pop_front();
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment