Skip to content

Commit df10fec

Browse files
committed
remove singleton
1 parent ef9e1b7 commit df10fec

File tree

5 files changed

+86
-149
lines changed

5 files changed

+86
-149
lines changed

orchagent/orch.cpp

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@ using namespace swss;
1717

1818
int gBatchSize = 0;
1919

20-
RingBuffer* Orch::gRingBuffer = nullptr;
21-
RingBuffer* Executor::gRingBuffer = nullptr;
20+
std::shared_ptr<RingBuffer> Orch::gRingBuffer = nullptr;
21+
std::shared_ptr<RingBuffer> Executor::gRingBuffer = nullptr;
22+
23+
RingBuffer::RingBuffer(int size): buffer(size)
24+
{
25+
if (size <= 1) {
26+
throw std::invalid_argument("Buffer size must be greater than 1");
27+
}
28+
}
2229

2330
void RingBuffer::pauseThread()
2431
{
@@ -35,17 +42,6 @@ void RingBuffer::notify()
3542
cv.notify_all();
3643
}
3744

38-
RingBuffer* RingBuffer::instance = nullptr;
39-
40-
RingBuffer* RingBuffer::get()
41-
{
42-
if (instance == nullptr) {
43-
instance = new RingBuffer();
44-
SWSS_LOG_NOTICE("Orchagent RingBuffer created at %p!", (void *)instance);
45-
}
46-
return instance;
47-
}
48-
4945
void RingBuffer::setIdle(bool idle)
5046
{
5147
idle_status = idle;
@@ -58,7 +54,7 @@ bool RingBuffer::IsIdle() const
5854

5955
bool RingBuffer::IsFull() const
6056
{
61-
return (tail + 1) % RING_SIZE == head;
57+
return (tail + 1) % static_cast<int>(buffer.size()) == head;
6258
}
6359

6460
bool RingBuffer::IsEmpty() const
@@ -71,7 +67,7 @@ bool RingBuffer::push(AnyTask ringEntry)
7167
if (IsFull())
7268
return false;
7369
buffer[tail] = std::move(ringEntry);
74-
tail = (tail + 1) % RING_SIZE;
70+
tail = (tail + 1) % static_cast<int>(buffer.size());
7571
return true;
7672
}
7773

@@ -80,7 +76,7 @@ bool RingBuffer::pop(AnyTask& ringEntry)
8076
if (IsEmpty())
8177
return false;
8278
ringEntry = std::move(buffer[head]);
83-
head = (head + 1) % RING_SIZE;
79+
head = (head + 1) % static_cast<int>(buffer.size());
8480
return true;
8581
}
8682

@@ -94,18 +90,6 @@ bool RingBuffer::serves(const std::string& tableName)
9490
return m_consumerSet.find(tableName) != m_consumerSet.end();
9591
}
9692

97-
void RingBuffer::release()
98-
{
99-
if (instance)
100-
delete instance;
101-
instance = nullptr;
102-
}
103-
RingBuffer* RingBuffer::reset()
104-
{
105-
release();
106-
return get();
107-
}
108-
10993
Orch::Orch(DBConnector *db, const string tableName, int pri)
11094
{
11195
addConsumer(db, tableName, pri);

orchagent/orch.h

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class Executor : public swss::Selectable
134134
}
135135

136136
Orch *getOrch() const { return m_orch; }
137-
static RingBuffer* gRingBuffer;
137+
static std::shared_ptr<RingBuffer> gRingBuffer;
138138
void pushRingBuffer(AnyTask&& func);
139139

140140
protected:
@@ -187,7 +187,6 @@ class ConsumerBase : public Executor {
187187
class RingBuffer
188188
{
189189
private:
190-
static RingBuffer* instance;
191190
std::vector<AnyTask> buffer;
192191
int head = 0;
193192
int tail = 0;
@@ -197,22 +196,8 @@ class RingBuffer
197196
std::mutex mtx;
198197
bool idle_status = true;
199198

200-
protected:
201-
RingBuffer(): buffer(RING_SIZE) {}
202-
~RingBuffer() {
203-
instance = nullptr;
204-
}
205-
206199
public:
207-
RingBuffer(const RingBuffer&) = delete;
208-
RingBuffer(RingBuffer&&) = delete;
209-
RingBuffer& operator= (const RingBuffer&) = delete;
210-
RingBuffer& operator= (RingBuffer&&) = delete;
211-
212-
static void release();
213-
static RingBuffer* reset();
214-
static RingBuffer* get();
215-
200+
RingBuffer(int size=RING_SIZE);
216201
bool thread_created = false;
217202
std::atomic<bool> thread_exited{false};
218203

@@ -290,7 +275,7 @@ class Orch
290275
Orch(const std::vector<TableConnector>& tables);
291276
virtual ~Orch() = default;
292277

293-
static RingBuffer* gRingBuffer;
278+
static std::shared_ptr<RingBuffer> gRingBuffer;
294279

295280
std::vector<swss::Selectable*> getSelectables();
296281

orchagent/orchdaemon.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,14 @@ void OrchDaemon::popRingBuffer()
145145
* This function initializes gRingBuffer, otherwise it's nullptr.
146146
*/
147147
void OrchDaemon::enableRingBuffer() {
148-
gRingBuffer = RingBuffer::get();
148+
gRingBuffer = std::make_shared<RingBuffer>();
149149
Executor::gRingBuffer = gRingBuffer;
150150
Orch::gRingBuffer = gRingBuffer;
151+
SWSS_LOG_NOTICE("RingBuffer created at %p!", (void *)gRingBuffer.get());
151152
}
152153

153154
void OrchDaemon::disableRingBuffer() {
154-
RingBuffer::release();
155+
gRingBuffer = nullptr;
155156
Executor::gRingBuffer = nullptr;
156157
Orch::gRingBuffer = nullptr;
157158
}

orchagent/orchdaemon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class OrchDaemon
9898
*/
9999
void popRingBuffer();
100100

101-
RingBuffer* gRingBuffer = nullptr;
101+
std::shared_ptr<RingBuffer> gRingBuffer = nullptr;
102102

103103
std::thread ring_thread;
104104

tests/mock_tests/orchdaemon_ut.cpp

Lines changed: 67 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
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

Comments
 (0)