diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index 707aa4af8..b9b88f87c 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -52,6 +52,9 @@ SrsResourceManager::SrsResourceManager(const std::string& label, bool verbose) trd = NULL; p_disposing_ = NULL; removing_ = false; + + nn_level0_cache_ = 100000; + conns_level0_cache_ = new SrsResourceFastIdItem[nn_level0_cache_]; } SrsResourceManager::~SrsResourceManager() @@ -65,6 +68,8 @@ SrsResourceManager::~SrsResourceManager() } clear(); + + srs_freepa(conns_level0_cache_); } srs_error_t SrsResourceManager::start() @@ -114,10 +119,14 @@ srs_error_t SrsResourceManager::cycle() return err; } -void SrsResourceManager::add(ISrsResource* conn) +void SrsResourceManager::add(ISrsResource* conn, bool* exists) { if (std::find(conns_.begin(), conns_.end(), conn) == conns_.end()) { conns_.push_back(conn); + } else { + if (exists) { + *exists = false; + } } } @@ -129,8 +138,35 @@ void SrsResourceManager::add_with_id(const std::string& id, ISrsResource* conn) void SrsResourceManager::add_with_fast_id(uint64_t id, ISrsResource* conn) { - add(conn); + bool exists = false; + add(conn, &exists); conns_fast_id_[id] = conn; + + if (exists) { + return; + } + + // For new resource, build the level-0 cache for fast-id. + SrsResourceFastIdItem* item = &conns_level0_cache_[(id | id>>32) % nn_level0_cache_]; + + // Ignore if exits item. + if (item->fast_id && item->fast_id == id) { + return; + } + + // Fresh one, create the item. + if (!item->fast_id) { + item->fast_id = id; + item->impl = conn; + item->nn_collisions = 1; + item->available = true; + } + + // Collision, increase the collisions. + if (item->fast_id != id) { + item->nn_collisions++; + item->available = false; + } } void SrsResourceManager::add_with_name(const std::string& name, ISrsResource* conn) @@ -152,6 +188,11 @@ ISrsResource* SrsResourceManager::find_by_id(std::string id) ISrsResource* SrsResourceManager::find_by_fast_id(uint64_t id) { + SrsResourceFastIdItem* item = &conns_level0_cache_[(id | id>>32) % nn_level0_cache_]; + if (item->available && item->fast_id == id) { + return item->impl; + } + map::iterator it = conns_fast_id_.find(id); return (it != conns_fast_id_.end())? it->second : NULL; } @@ -332,6 +373,15 @@ void SrsResourceManager::dispose(ISrsResource* c) if (c != it->second) { ++it; } else { + // Update the level-0 cache for fast-id. + uint64_t id = it->first; + SrsResourceFastIdItem* item = &conns_level0_cache_[(id | id>>32) % nn_level0_cache_]; + item->nn_collisions--; + if (!item->nn_collisions) { + item->fast_id = 0; + item->available = false; + } + // Use C++98 style: https://stackoverflow.com/a/4636230 conns_fast_id_.erase(it++); } diff --git a/trunk/src/app/srs_app_conn.hpp b/trunk/src/app/srs_app_conn.hpp index 8af94e956..db10ee423 100644 --- a/trunk/src/app/srs_app_conn.hpp +++ b/trunk/src/app/srs_app_conn.hpp @@ -52,6 +52,27 @@ public: virtual void on_disposing(ISrsResource* c) = 0; }; +// The item to identify the fast id object. +class SrsResourceFastIdItem +{ +public: + // If available, use the resource in item. + bool available; + // How many resource have the same fast-id, which contribute a collision. + int nn_collisions; + // The first fast-id of resources. + uint64_t fast_id; + // The first resource object. + ISrsResource* impl; +public: + SrsResourceFastIdItem() { + available = false; + nn_collisions = 0; + fast_id = 0; + impl = NULL; + } +}; + // The resource manager remove resource and delete it asynchronously. class SrsResourceManager : virtual public ISrsCoroutineHandler, virtual public ISrsResourceManager { @@ -78,6 +99,9 @@ private: std::map conns_id_; // The connections with resource fast(int) id. std::map conns_fast_id_; + // The level-0 fast cache for fast id. + int nn_level0_cache_; + SrsResourceFastIdItem* conns_level0_cache_; // The connections with resource name. std::map conns_name_; public: @@ -91,7 +115,7 @@ public: public: virtual srs_error_t cycle(); public: - void add(ISrsResource* conn); + void add(ISrsResource* conn, bool* exists = NULL); void add_with_id(const std::string& id, ISrsResource* conn); void add_with_fast_id(uint64_t id, ISrsResource* conn); void add_with_name(const std::string& name, ISrsResource* conn); diff --git a/trunk/src/utest/srs_utest_app.cpp b/trunk/src/utest/srs_utest_app.cpp index 010ed4272..a0ad704f7 100644 --- a/trunk/src/utest/srs_utest_app.cpp +++ b/trunk/src/utest/srs_utest_app.cpp @@ -30,6 +30,119 @@ using namespace std; #include #include +#include +#include + +class MockIDResource : public ISrsResource +{ +public: + int id; + MockIDResource(int v) { + id = v; + } + virtual ~MockIDResource() { + } + virtual const SrsContextId& get_id() { + return _srs_context->get_id(); + } + virtual std::string desc() { + return ""; + } +}; + +VOID TEST(AppResourceManagerTest, FindByFastID) +{ + srs_error_t err = srs_success; + + if (true) { + SrsResourceManager m("test"); + HELPER_EXPECT_SUCCESS(m.start()); + + m.add_with_fast_id(101, new MockIDResource(1)); + m.add_with_fast_id(102, new MockIDResource(2)); + m.add_with_fast_id(103, new MockIDResource(3)); + EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); + EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(102))->id); + EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(103))->id); + } + + if (true) { + SrsResourceManager m("test"); + HELPER_EXPECT_SUCCESS(m.start()); + + MockIDResource* r1 = new MockIDResource(1); + MockIDResource* r2 = new MockIDResource(2); + MockIDResource* r3 = new MockIDResource(3); + m.add_with_fast_id(101, r1); + m.add_with_fast_id(102, r2); + m.add_with_fast_id(103, r3); + EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); + EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(102))->id); + EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(103))->id); + + m.remove(r2); srs_usleep(0); + EXPECT_TRUE(m.find_by_fast_id(102) == NULL); + } + + if (true) { + SrsResourceManager m("test"); + HELPER_EXPECT_SUCCESS(m.start()); + + MockIDResource* r1 = new MockIDResource(1); + MockIDResource* r2 = new MockIDResource(2); + MockIDResource* r3 = new MockIDResource(3); + m.add_with_fast_id(1, r1); + m.add_with_fast_id(100001, r2); + m.add_with_fast_id(1000001, r3); + EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(1))->id); + EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(100001))->id); + EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(1000001))->id); + + m.remove(r2); srs_usleep(0); + EXPECT_TRUE(m.find_by_fast_id(100001) == NULL); + + m.remove(r3); srs_usleep(0); + EXPECT_TRUE(m.find_by_fast_id(1000001) == NULL); + + m.remove(r1); srs_usleep(0); + EXPECT_TRUE(m.find_by_fast_id(1) == NULL); + } + + if (true) { + SrsResourceManager m("test"); + HELPER_EXPECT_SUCCESS(m.start()); + + m.add_with_fast_id(101, new MockIDResource(1)); + m.add_with_fast_id(10101, new MockIDResource(2)); + m.add_with_fast_id(1010101, new MockIDResource(3)); + m.add_with_fast_id(101010101, new MockIDResource(4)); + m.add_with_fast_id(10101010101LL, new MockIDResource(5)); + m.add_with_fast_id(1010101010101LL, new MockIDResource(6)); + m.add_with_fast_id(101010101010101LL, new MockIDResource(7)); + m.add_with_fast_id(10101010101010101LL, new MockIDResource(8)); + m.add_with_fast_id(1010101010101010101ULL, new MockIDResource(9)); + m.add_with_fast_id(11010101010101010101ULL, new MockIDResource(10)); + EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); + EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(10101))->id); + EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(1010101))->id); + EXPECT_EQ(4, ((MockIDResource*)m.find_by_fast_id(101010101))->id); + EXPECT_EQ(5, ((MockIDResource*)m.find_by_fast_id(10101010101LL))->id); + EXPECT_EQ(6, ((MockIDResource*)m.find_by_fast_id(1010101010101LL))->id); + EXPECT_EQ(7, ((MockIDResource*)m.find_by_fast_id(101010101010101LL))->id); + EXPECT_EQ(8, ((MockIDResource*)m.find_by_fast_id(10101010101010101LL))->id); + EXPECT_EQ(9, ((MockIDResource*)m.find_by_fast_id(1010101010101010101ULL))->id); + EXPECT_EQ(10, ((MockIDResource*)m.find_by_fast_id(11010101010101010101ULL))->id); + } + + if (true) { + SrsResourceManager m("test"); + HELPER_EXPECT_SUCCESS(m.start()); + + m.add_with_fast_id(101, new MockIDResource(1)); + m.add_with_fast_id(101, new MockIDResource(4)); + EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); + } +} VOID TEST(AppCoroutineTest, Dummy) {