1+ #define protected public
2+ #include " orch.h"
13#include " orchdaemon.h"
4+ #undef protected
25#include " dbconnector.h"
36#include < gtest/gtest.h>
47#include < gmock/gmock.h>
@@ -26,11 +29,6 @@ namespace orchdaemon_test
2629
2730 OrchDaemon* orchd;
2831
29- RingBuffer* gRingBuffer = RingBuffer::get();
30-
31- std::shared_ptr<Consumer> consumer;
32-
33- std::shared_ptr<Orch> orch;
3432 OrchDaemonTest ()
3533 {
3634 mock_sai_switch = &mock_sai_switch_;
@@ -48,14 +46,6 @@ namespace orchdaemon_test
4846 delete orchd;
4947 };
5048
51- void SetUp () override {
52- gRingBuffer = RingBuffer::reset ();
53- }
54-
55- void TearDown () override
56- {
57- RingBuffer::release ();
58- }
5949 };
6050
6151 TEST_F (OrchDaemonTest, logRotate)
@@ -65,137 +55,114 @@ namespace orchdaemon_test
6555 orchd->logRotate ();
6656 }
6757
58+ TEST_F (OrchDaemonTest, ringBuffer)
59+ {
60+ int test_ring_size = 2 ;
61+
62+ auto ring = new RingBuffer (test_ring_size);
63+
64+ for (int i = 0 ; i < test_ring_size - 1 ; i++)
65+ {
66+ EXPECT_TRUE (ring->push ([](){}));
67+ }
68+ EXPECT_FALSE (ring->push ([](){}));
69+
70+ AnyTask task;
71+ for (int i = 0 ; i < test_ring_size - 1 ; i++)
72+ {
73+ EXPECT_TRUE (ring->pop (task));
74+ }
75+
76+ EXPECT_FALSE (ring->pop (task));
77+
78+ ring->setIdle (true );
79+ EXPECT_TRUE (ring->IsIdle ());
80+ delete ring;
81+ }
82+
6883 TEST_F (OrchDaemonTest, RingThread)
6984 {
7085 orchd->enableRingBuffer ();
7186
87+ // verify ring buffer is created
7288 EXPECT_TRUE (Executor::gRingBuffer != nullptr );
7389 EXPECT_TRUE (Executor::gRingBuffer == Orch::gRingBuffer );
7490
7591 orchd->ring_thread = std::thread (&OrchDaemon::popRingBuffer, orchd);
92+ auto gRingBuffer = orchd->gRingBuffer ;
7693
77- while (!RingBuffer::get ()->thread_created )
94+ // verify ring_thread is created
95+ while (!gRingBuffer ->thread_created )
7896 {
7997 std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
8098 }
8199
82100 bool task_executed = false ;
83101 AnyTask task = [&task_executed]() { task_executed = true ;};
84- RingBuffer::get () ->push (task);
102+ gRingBuffer ->push (task);
85103
86- EXPECT_TRUE (RingBuffer::get ()->IsIdle ());
104+ // verify ring thread is conditional locked
105+ EXPECT_TRUE (gRingBuffer ->IsIdle ());
106+ EXPECT_FALSE (task_executed);
87107
88- RingBuffer::get () ->notify ();
108+ gRingBuffer ->notify ();
89109
90- while (!RingBuffer::get ()->IsEmpty () || !RingBuffer::get ()->IsIdle ())
110+ // verify notify() would activate the ring thread when buffer is not empty
111+ while (!gRingBuffer ->IsEmpty () || !gRingBuffer ->IsIdle ())
91112 {
92113 std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
93114 }
94115
95116 EXPECT_TRUE (task_executed);
96117
97- EXPECT_TRUE (orchd->ring_thread .joinable ());
98-
99118 delete orchd;
100119
120+ // verify the destructor of orchdaemon will stop the ring thread
101121 EXPECT_FALSE (orchd->ring_thread .joinable ());
122+ // verify the destructor of orchdaemon also resets ring buffer
123+ EXPECT_TRUE (Executor::gRingBuffer == nullptr );
102124
103- // reset the orchd
125+ // reset the orchd for other testcases
104126 orchd = new OrchDaemon (&appl_db, &config_db, &state_db, &counters_db, nullptr );
105127 }
106128
107- TEST_F (OrchDaemonTest, PushAnyTask )
129+ TEST_F (OrchDaemonTest, PushRingBuffer )
108130 {
109131 orchd->enableRingBuffer ();
110132
111- orch = make_shared<Orch>(&appl_db, " ROUTE_TABLE" , 0 );
112- consumer = make_shared<Consumer>(new swss::ConsumerStateTable (&appl_db, " ROUTE_TABLE" , 128 , 1 ), orch.get (), " ROUTE_TABLE" );
133+ auto gRingBuffer = orchd->gRingBuffer ;
134+
135+ std::vector<std::string> tables = {" ROUTE_TABLE" , " OTHER_TABLE" };
136+ auto orch = make_shared<Orch>(&appl_db, tables);
137+ auto route_consumer = dynamic_cast <Consumer *>(orch->getExecutor (" ROUTE_TABLE" ));
138+ auto other_consumer = dynamic_cast <Consumer *>(orch->getExecutor (" OTHER_TABLE" ));
113139
114140 EXPECT_TRUE (gRingBuffer ->serves (" ROUTE_TABLE" ));
115141 EXPECT_FALSE (gRingBuffer ->serves (" OTHER_TABLE" ));
116- EXPECT_TRUE (gRingBuffer ->IsEmpty ());
117-
118- int x = 1 ;
119- int y = 3 ;
120- AnyTask t1 = [&](){x=2 ;};
121- AnyTask t2 = [](){};
122- AnyTask t3 = [&](){x=3 ;y=2 ;};
123-
124- gRingBuffer ->push (t1);
125- gRingBuffer ->push (t2);
126- EXPECT_FALSE (gRingBuffer ->IsEmpty ());
127142
128- gRingBuffer -> pop (t3) ;
129- t3 ( );
130- EXPECT_TRUE (x== 2 );
131- EXPECT_TRUE (y ==3 );
143+ int x = 0 ;
144+ route_consumer-> pushRingBuffer ([&](){x= 3 ;} );
145+ // verify `pushRingBuffer` is equivalent to executing the task immediately
146+ EXPECT_TRUE (gRingBuffer -> IsEmpty () && gRingBuffer -> IsIdle () && ! gRingBuffer -> thread_created && x ==3 );
132147
133- EXPECT_TRUE (gRingBuffer ->pop (t3));
134- EXPECT_FALSE (gRingBuffer ->pop (t3));
148+ gRingBuffer ->thread_created = true ; // set the flag to assume the ring thread is created (actually not)
135149
136- consumer->pushRingBuffer ([&](){x=3 ;});
137- EXPECT_TRUE (x==3 );
150+ // verify `pushRingBuffer` is equivalent to executing the task immediately when ring is empty and idle
151+ other_consumer->pushRingBuffer ([&](){x=4 ;});
152+ EXPECT_TRUE (gRingBuffer ->IsEmpty () && gRingBuffer ->IsIdle () && x==4 );
138153
139- gRingBuffer ->thread_created = true ;
140- consumer->pushRingBuffer ([&](){x=4 ;});
141- EXPECT_TRUE (x==3 );
154+ route_consumer->pushRingBuffer ([&](){x=5 ;});
155+ // verify `pushRingBuffer` would not execute the task if thread_created is true
156+ // it only pushes the task to the ring buffer, without executing it
157+ EXPECT_TRUE (!gRingBuffer ->IsEmpty () && x==4 );
142158
143- gRingBuffer ->pop (t3);
144- t3 ();
145- EXPECT_TRUE (x==4 );
159+ AnyTask task;
160+ gRingBuffer ->pop (task);
161+ task ();
162+ // hence the task needs to be popped and explicitly executed
163+ EXPECT_TRUE (gRingBuffer ->IsEmpty () && x==5 );
146164
147165 orchd->disableRingBuffer ();
148166 }
149167
150- TEST_F (OrchDaemonTest, ThreadPauseAndNotify) {
151-
152- bool thread_finished = false ;
153- std::thread t ([this , &thread_finished]() {
154- gRingBuffer ->setIdle (true );
155- gRingBuffer ->pauseThread ();
156- thread_finished = true ;
157- });
158- std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
159-
160- AnyTask task = []() { };
161- EXPECT_TRUE (gRingBuffer ->push (task));
162- gRingBuffer ->notify ();
163-
164- t.join ();
165- EXPECT_TRUE (thread_finished);
166- }
167-
168- TEST_F (OrchDaemonTest, MultiThread) {
169- std::vector<std::thread> producers;
170- std::vector<std::thread> consumers;
171-
172- for (int i = 0 ; i < 3 ; i++) {
173- producers.emplace_back ([this ]() {
174- AnyTask task = []() { };
175- for (int j = 0 ; j < 10 ; j++) {
176- gRingBuffer ->push (task);
177- std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
178- }
179- });
180- }
181-
182- for (int i = 0 ; i < 3 ; i++) {
183- consumers.emplace_back ([this ]() {
184- for (int j = 0 ; j < 10 ; j++) {
185- AnyTask task;
186- while (!gRingBuffer ->pop (task)) {
187- std::this_thread::sleep_for (std::chrono::milliseconds (1 ));
188- }
189- }
190- });
191- }
192-
193- for (auto & t : producers) {
194- t.join ();
195- }
196- for (auto & t : consumers) {
197- t.join ();
198- }
199- }
200-
201168}
0 commit comments