From 4edafbda3250b8af3cb1a1587675d793b8fa2535 Mon Sep 17 00:00:00 2001 From: ubuntu14 Date: Sat, 14 Nov 2015 06:15:22 -0800 Subject: [PATCH] add some functions in class dbuf_guard for convinence and safty --- lib_acl_cpp/changes.txt | 5 + .../include/acl_cpp/stdlib/dbuf_pool.hpp | 83 +++++++- lib_acl_cpp/samples/dbuf/dbuf2/main.cpp | 193 ++++++++++++++++-- lib_acl_cpp/src/stdlib/dbuf_pool.cpp | 86 ++++++-- 4 files changed, 316 insertions(+), 51 deletions(-) diff --git a/lib_acl_cpp/changes.txt b/lib_acl_cpp/changes.txt index 1ae8747d3..1566e05ff 100644 --- a/lib_acl_cpp/changes.txt +++ b/lib_acl_cpp/changes.txt @@ -1,6 +1,11 @@ 修改历史列表: ------------------------------------------------------------------------ +376) 2015.11.14 +376.1) feature: dbuf_guard 类增加了更多方便操作的方法,同时使数组对象也在 +内存池对象上创建 +376.2) safety: dbuf_guard 类中增加了对 dbuf_obj 对象的安全保护 + 375) 2015.11.13 375.1) feature: 连接池模块简化接口设计,允许连接集群管理器在添加新的连接池时设置超时时间, 从而使连接池集群管理器针对不同的服务器地址采用不同的超时时间,这样可以使连接池 diff --git a/lib_acl_cpp/include/acl_cpp/stdlib/dbuf_pool.hpp b/lib_acl_cpp/include/acl_cpp/stdlib/dbuf_pool.hpp index a8ed1e254..eb42a59cd 100644 --- a/lib_acl_cpp/include/acl_cpp/stdlib/dbuf_pool.hpp +++ b/lib_acl_cpp/include/acl_cpp/stdlib/dbuf_pool.hpp @@ -170,11 +170,36 @@ public: virtual ~dbuf_obj() {} + /** + * 获得该对象在 dbuf_guard 中的数组中的下标位置 + * @return {int} 返回该对象在 dbuf_guard 中的数组中的下标位置,当该 + * 对象没有被 dbuf_guard 保存时,则返回 -1,此时有可能会造成内存泄露 + */ + int pos() const + { + return pos_; + } + + /** + * 返回构造函数中 dbuf_guard 对象 + * @return {dbuf_guard*} + */ + dbuf_guard* get_guard() const + { + return guard_; + } + private: friend class dbuf_guard; + // 记录本对象所属的 dbuf_guard 对象 + dbuf_guard* guard_; + // 该变量便于 dbuf_guard 对象使用,以增加安全性 int nrefer_; + + // 该对象在 dbuf_guard 对象中记录的数组的位置 + int pos_; }; /** @@ -188,10 +213,17 @@ public: * 构造函数 * @param dbuf {dbuf_pool*} 当该内存池对象非空时,dbuf 将由 dbuf_guard * 接管,如果为空,则本构造函数内部将会自动创建一个 dbuf_pool 对象 - * @param nblock {size_t} 当 dbuf 参数为 NULL 时,本类对象内部创建 - * dbuf_pool 对象时,本参数指定了内存块(4096)的倍数 + * @param capacity {size_t} 内部创建的 objs_ 数组的初始长度 */ - dbuf_guard(dbuf_pool* dbuf = NULL, size_t nblock = 2); + dbuf_guard(dbuf_pool* dbuf, size_t capacity = 100); + + /** + * 构造函数 + * @param nblock {size_t} 本类对象内部创建 dbuf_pool 对象时,本参数 + * 指定了内存块(4096)的倍数 + * @param capacity {size_t} 内部创建的 objs_ 数组的初始长度 + */ + dbuf_guard(size_t nblock = 2, size_t capacity = 100); /** * 析构函数,在析构函数内部将会自动销毁由构造函数传入的 dbuf_pool 对象 @@ -301,10 +333,12 @@ public: /** * 可以手动调用本函数,将在 dbuf_pool 上分配的 dbuf_obj 子类对象交给 - * dbuf_guard 对象统一进行销毁管理 + * dbuf_guard 对象统一进行销毁管理;严禁将同一个 dbuf_obj 子类对象同 + * 时将给多个 dbuf_guard 对象进行管理,否则将会产生对象的重复释放 * @param obj {dbuf_obj*} * @return {int} 返回 obj 被添加后其在 dbuf_obj 对象数组中的下标位置, - * 如果返回值 < 0 则说明输入的对象已经在 dbuf_guard 的监管中 + * dbuf_guard 内部对 dbuf_obj 对象的管理具有防重添加机制,所以当多次 + * 将同一个 dbuf_obj 对象置入同一个 dbuf_guard 对象时,内部只会放一次 */ int push_back(dbuf_obj* obj); @@ -314,14 +348,16 @@ public: */ size_t size() const { - return objs_.size(); + return size_; } /** - * 获得当前内存池中管理的对象集合 - * @return {std::vector&} + * 获得当前内存池中管理的对象集合,该函数必须与 size() 函数配合使用, + * 以免指针地址越界 + * @return {dbuf_obj**} 返回 dbuf_obj 对象集合,永远返回非 NULL 值, + * 该数组的大小由 size() 函数决定 */ - const std::vector& get_objs() const + dbuf_obj** get_objs() const { return objs_; } @@ -333,9 +369,34 @@ public: */ dbuf_obj* operator[](size_t pos) const; + /** + * 返回指定下标的对象 + * @param pos {size_t} 指定对象的下标位置,不应越界 + * @return {dbuf_obj*} 当下标位置越界时返回 NULL + */ + dbuf_obj* get(size_t pos) const; + + /** + * 设置内建 objs_ 数组对象每次在扩充空间时的增量,内部缺省值为 100 + * @param incr {size_t} + */ + void set_increment(size_t incr); + private: - dbuf_pool* dbuf_; - std::vector objs_; + size_t nblock_; // 内部自建 dbuf_pool 内存块的单位个数 + dbuf_pool* dbuf_; // 内存池对象 + + // 此处之所以使用自实现的 dbuf_obj 数组对象,而没有使用 std::vector, + // 一方面使数组对象也在 dbuf_pool 内存池上创建,另一方面可以避免 + // std::vector 内部在扩容时的内存不可控性 + + dbuf_obj** objs_; // 存储 dbuf_obj 对象的数组对象 + size_t size_; // 存储于 objs_ 中的对象个数 + size_t capacity_; // objs_ 数组的大小 + size_t incr_; // objs_ 数组扩充时的增量个数 + + // 扩充 objs_ 数组对象的空间 + void extend_objs(); }; /** diff --git a/lib_acl_cpp/samples/dbuf/dbuf2/main.cpp b/lib_acl_cpp/samples/dbuf/dbuf2/main.cpp index 79826355b..616ea6a31 100644 --- a/lib_acl_cpp/samples/dbuf/dbuf2/main.cpp +++ b/lib_acl_cpp/samples/dbuf/dbuf2/main.cpp @@ -3,6 +3,9 @@ #include "stdafx.h" #include +/** + * dbuf_obj 子类,在 dbuf_pool 上动态分配,由 dbuf_guard 统一进行管理 + */ class myobj : public acl::dbuf_obj { public: @@ -13,46 +16,192 @@ public: void run() { - printf("----> hello world <-----\r\n"); + printf("----> run->hello world <-----\r\n"); } private: char* ptr_; + // 将析构声明为私人,以强制要求该对象被动态分配,该析构函数将由 + // dbuf_guard 统一调用,以释放本类对象中产生的动态内存(ptr_) ~myobj() { free(ptr_); - printf("----> myobj destroied <-----\r\n"); } }; +static void test_dbuf(acl::dbuf_guard& dbuf) +{ + for (int i = 0; i < 102400; i++) + { + // 动态分配内存 + char* ptr = (char*) dbuf.dbuf_alloc(10); + (void) ptr; + } + + for (int i = 0; i < 102400; i++) + { + // 动态分配字符串 + char* str = dbuf.dbuf_strdup("hello world"); + if (i < 5) + printf(">>str->%s\r\n", str); + } + + // 动态分配内存 + + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(2048); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(1024); + (void) dbuf.dbuf_alloc(10240); + + for (int i = 0; i < 10000; i++) + { + // 动态分配 dbuf_obj 子类对象,并通过将 dbuf_guard 对象传入 + // dbuf_obj 的构造函数,从而将之由 dbuf_guard 统一管理, + + myobj* obj = new (dbuf.dbuf_alloc(sizeof(myobj))) myobj(&dbuf); + + // 验证 dbuf_obj 对象在 dbuf_guard 中的在在一致性 + assert(obj == dbuf[obj->pos()]); + + // 调用 dbuf_obj 子类对象 myobj 的函数 run + if (i < 10) + obj->run(); + } + + for (int i = 0; i < 10000; i++) + { + myobj* obj = new (dbuf.dbuf_alloc(sizeof(myobj))) myobj(NULL); + + int pos = dbuf.push_back(obj); + assert(dbuf[pos] == obj); + + if (i < 10) + obj->run(); + } + + for (int i = 0; i < 10000; i++) + { + myobj* obj = new (dbuf.dbuf_alloc(sizeof(myobj))) myobj(&dbuf); + + // 虽然多次将 dbuf_obj 对象置入 dbuf_guard 中,因为 dbuf_obj + // 内部的引用计数,所以可以防止被重复添加 + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + + assert(obj == dbuf[obj->pos()]); + + if (i < 10) + obj->run(); + } + + for (int i = 0; i < 10000; i++) + { + myobj* obj = new (dbuf.dbuf_alloc(sizeof(myobj))) myobj(NULL); + + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + (void) dbuf.push_back(obj); + + int pos = dbuf.push_back(obj); + assert(dbuf[pos] == obj); + + if (i < 10) + obj->run(); + } +} + +static void wait_pause() +{ + printf("Enter any key to continue ..."); + fflush(stdout); + getchar(); +} + +static void test1() +{ + // dbuf_gaurd 对象创建在栈上,函数返回前该对象自动销毁 + acl::dbuf_guard dbuf; + + test_dbuf(dbuf); +} + +static void test2() +{ + // 动态创建 dbuf_guard 对象,需要手动销毁该对象 + acl::dbuf_guard* dbuf = new acl::dbuf_guard; + + test_dbuf(*dbuf); + + // 手工销毁该对象 + delete dbuf; +} + +static void test3() +{ + // 将内存池对象 dbuf_pool 做为 dbuf_guard 构造函数参数传入,当 + // dbuf_guard 对象销毁时,dbuf_pool 对象一同被销毁 + acl::dbuf_guard dbuf(new acl::dbuf_pool); + + test_dbuf(dbuf); +} + +static void test4() +{ + // 动态创建 dbuf_guard 对象,同时指定内存池中内存块的分配倍数为 10, + // 即指定内部每个内存块大小为 4096 * 10 = 40 KB,同时 + // 指定内部动态数组的初始容量大小 + acl::dbuf_guard dbuf(10, 100); + + test_dbuf(dbuf); +} + +static void test5() +{ + acl::dbuf_pool* dp = new acl::dbuf_pool; + + // 在内存池对象上动态创建 dbuf_guard 对象,这样可以将内存分配的次数 + // 进一步减少一次 + acl::dbuf_guard* dbuf = new (dp->dbuf_alloc(sizeof(acl::dbuf_guard))) + acl::dbuf_guard(dp); + + test_dbuf(*dbuf); + + // 因为 dbuf_gaurd 对象也是在 dbuf_pool 内存池对象上动态创建的,所以 + // 只能通过直接调用 dbuf_guard 的析构函数来释放所有的内存对象; + // 既不能直接 dbuf_pool->desotry(),也不能直接 delete dbuf_guard 来 + // 销毁 dbuf_guard 对象 + dbuf->~dbuf_guard(); +} + int main(void) { acl::log::stdout_open(true); - acl::dbuf_guard dbuf; + test1(); + wait_pause(); - for (int i = 0; i < 102400; i++) - dbuf.dbuf_alloc(10); + test2(); + wait_pause(); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(2048); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(1024); - dbuf.dbuf_alloc(10240); + test3(); + wait_pause(); - for (int i = 0; i < 10; i++) - { - myobj* obj = new (dbuf.dbuf_alloc(sizeof(myobj))) myobj(&dbuf); - obj->run(); - } + test4(); + wait_pause(); + test5(); return 0; } diff --git a/lib_acl_cpp/src/stdlib/dbuf_pool.cpp b/lib_acl_cpp/src/stdlib/dbuf_pool.cpp index 486f29553..1891c2936 100644 --- a/lib_acl_cpp/src/stdlib/dbuf_pool.cpp +++ b/lib_acl_cpp/src/stdlib/dbuf_pool.cpp @@ -89,51 +89,101 @@ bool dbuf_pool::dbuf_unkeep(const void* addr) ////////////////////////////////////////////////////////////////////////////// dbuf_obj::dbuf_obj(dbuf_guard* guard /* = NULL */) - : nrefer_(0) + : guard_(guard) + , nrefer_(0) + , pos_(-1) { if (guard) guard->push_back(this); } -dbuf_guard::dbuf_guard(acl::dbuf_pool* dbuf /* = NULL */, size_t nblock /* = 2 */) +dbuf_guard::dbuf_guard(acl::dbuf_pool* dbuf, size_t capacity /* = 100 */) + : nblock_(2) + , size_(0) + , capacity_(capacity == 0 ? 100 : capacity) + , incr_(100) { if (dbuf == NULL) - dbuf_ = new (nblock) acl::dbuf_pool; + dbuf_ = new (nblock_) acl::dbuf_pool; else dbuf_ = dbuf; + objs_ = (dbuf_obj**) dbuf_->dbuf_alloc(sizeof(dbuf_obj*) * capacity_); +} + +dbuf_guard::dbuf_guard(size_t nblock /* = 2 */, size_t capacity /* = 100 */) + : nblock_(nblock == 0 ? 2 : nblock) + , size_(0) + , capacity_(capacity == 0 ? 100 : capacity) + , incr_(100) +{ + dbuf_ = new (nblock_) acl::dbuf_pool; + objs_ = (dbuf_obj**) dbuf_->dbuf_alloc(sizeof(dbuf_obj*) * capacity_); } dbuf_guard::~dbuf_guard() { - for (std::vector::iterator it = objs_.begin(); - it != objs_.end(); ++it) - { - (*it)->~dbuf_obj(); - } + for (size_t i = 0; i < size_; i++) + objs_[i]->~dbuf_obj(); dbuf_->destroy(); } +void dbuf_guard::extend_objs() +{ + dbuf_obj** old_objs = objs_; + capacity_ += incr_; + objs_ = (dbuf_obj**) dbuf_->dbuf_alloc(sizeof(dbuf_obj*) * capacity_); + + for (size_t i = 0; i < size_; i++) + objs_[i] = old_objs[i]; +} + +void dbuf_guard::set_increment(size_t incr) +{ + if (incr > 0) + incr_ = incr; +} + int dbuf_guard::push_back(dbuf_obj* obj) { - if (obj->nrefer_ >= 1) + if (obj->nrefer_ < 1) { - logger_error("obj->nrefer_: %d >= 1", obj->nrefer_); - return -1; + if (obj->guard_ == NULL) + obj->guard_ = this; + else if (obj->guard_ != this) + { + logger_fatal("obj->guard_(%p) != me(%p), nrefer: %d", + obj->guard_, this, obj->nrefer_); + } + + if (size_ >= capacity_) + extend_objs(); + + objs_[size_] = obj; + obj->nrefer_++; + obj->pos_ = (int) size_; + size_++; + } + else if (obj->guard_ != this) + { + logger_fatal("obj->guard_(%p) != me(%p), nrefer: %d", + obj->guard_, this, obj->nrefer_); } - objs_.push_back(obj); - obj->nrefer_++; - - return (int) objs_.size() - 1; + return obj->pos_; } dbuf_obj* dbuf_guard::operator[](size_t pos) const { - if (pos >= objs_.size()) - return NULL; + return get(pos); +} - return objs_[pos]; +dbuf_obj* dbuf_guard::get(size_t pos) const +{ + if (pos >= size_) + return NULL; + else + return objs_[pos]; } } // namespace acl