Skip to content

Commit a8fd2ce

Browse files
committed
SharedMemory test.
1 parent fe0b31e commit a8fd2ce

File tree

3 files changed

+222
-2
lines changed

3 files changed

+222
-2
lines changed

builds/win32/msvc15/common_test.vcxproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
<ItemGroup>
252252
<ClCompile Include="..\..\..\src\common\tests\CommonTest.cpp" />
253253
<ClCompile Include="..\..\..\src\common\tests\CvtTest.cpp" />
254+
<ClCompile Include="..\..\..\src\common\tests\SharedMemoryTest.cpp" />
254255
<ClCompile Include="..\..\..\src\common\tests\StringTest.cpp" />
255256
<ClCompile Include="..\..\..\src\common\classes\tests\AlignerTest.cpp" />
256257
<ClCompile Include="..\..\..\src\common\classes\tests\ArrayTest.cpp" />
@@ -274,4 +275,4 @@
274275
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
275276
<ImportGroup Label="ExtensionTargets">
276277
</ImportGroup>
277-
</Project>
278+
</Project>

builds/win32/msvc15/common_test.vcxproj.filters

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,8 @@
4848
<ClCompile Include="..\..\..\src\yvalve\gds.cpp">
4949
<Filter>source</Filter>
5050
</ClCompile>
51+
<ClCompile Include="..\..\..\src\common\tests\SharedMemoryTest.cpp">
52+
<Filter>source</Filter>
53+
</ClCompile>
5154
</ItemGroup>
52-
</Project>
55+
</Project>

src/common/tests/SharedMemoryTest.cpp

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* The contents of this file are subject to the Initial
3+
* Developer's Public License Version 1.0 (the "License");
4+
* you may not use this file except in compliance with the
5+
* License. You may obtain a copy of the License at
6+
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
7+
*
8+
* Software distributed under the License is distributed AS IS,
9+
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing rights
11+
* and limitations under the License.
12+
*
13+
* The Original Code was created by Adriano dos Santos Fernandes
14+
* for the Firebird Open Source RDBMS project.
15+
*
16+
* Copyright (c) 2025 Adriano dos Santos Fernandes <adrianosf@gmail.com>
17+
* and all contributors signed below.
18+
*
19+
* All Rights Reserved.
20+
* Contributor(s): ______________________________________.
21+
*/
22+
23+
#include "firebird.h"
24+
#include "boost/test/unit_test.hpp"
25+
#include "../common/classes/auto.h"
26+
#include "../common/classes/fb_string.h"
27+
#include "../common/StatusArg.h"
28+
#include "../common/isc_s_proto.h"
29+
#include "../common/isc_proto.h"
30+
#include <atomic>
31+
#include <chrono>
32+
#include <string>
33+
#include <thread>
34+
#include <vector>
35+
36+
using namespace Firebird;
37+
using namespace std::chrono_literals;
38+
39+
40+
namespace
41+
{
42+
class IpcObjectImpl final : public IpcObject
43+
{
44+
public:
45+
struct Header : public MemoryHeader
46+
{
47+
event_t receiverEvent;
48+
event_t senderEvent;
49+
unsigned data;
50+
};
51+
52+
public:
53+
explicit IpcObjectImpl(const std::string aPhysicalName)
54+
: physicalName(aPhysicalName),
55+
sharedMemory(physicalName.c_str(), sizeof(Header), this)
56+
{
57+
checkHeader(sharedMemory.getHeader());
58+
}
59+
60+
~IpcObjectImpl()
61+
{
62+
const auto header = sharedMemory.getHeader();
63+
64+
if (header->receiverEvent.event_pid == 0 && header->senderEvent.event_pid == 0)
65+
sharedMemory.removeMapFile();
66+
}
67+
68+
IpcObjectImpl(const IpcObjectImpl&) = delete;
69+
IpcObjectImpl& operator=(const IpcObjectImpl&) = delete;
70+
71+
public:
72+
bool initialize(SharedMemoryBase* sm, bool init) override
73+
{
74+
if (init)
75+
{
76+
const auto header = reinterpret_cast<Header*>(sm->sh_mem_header);
77+
78+
// Initialize the shared data header.
79+
initHeader(header);
80+
}
81+
82+
return true;
83+
}
84+
85+
void mutexBug(int osErrorCode, const char* text) override
86+
{
87+
iscLogStatus(("Error when working with " + physicalName).c_str(),
88+
(Arg::Gds(isc_sys_request) << text << Arg::OsError(osErrorCode)).value());
89+
}
90+
91+
USHORT getType() const override
92+
{
93+
return 1;
94+
}
95+
96+
USHORT getVersion() const override
97+
{
98+
return 1;
99+
}
100+
101+
const char* getName() const override
102+
{
103+
return "IpcObjectImpl test";
104+
}
105+
106+
public:
107+
const std::string physicalName;
108+
SharedMemory<Header> sharedMemory;
109+
};
110+
}
111+
112+
113+
static std::string getTempPath()
114+
{
115+
static std::atomic<int> counter{0};
116+
117+
const auto now = std::chrono::system_clock::now();
118+
const auto nowNs = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
119+
120+
return "shared_memory_test_" +
121+
std::to_string(nowNs) + "_" +
122+
std::to_string(counter.fetch_add(1));
123+
}
124+
125+
126+
BOOST_AUTO_TEST_SUITE(CommonSuite)
127+
BOOST_AUTO_TEST_SUITE(SharedMemorySuite)
128+
129+
130+
BOOST_AUTO_TEST_CASE(SharedMemoryTest)
131+
{
132+
constexpr unsigned PRODUCER_COUNT = 2;
133+
134+
const auto testPath = getTempPath();
135+
std::vector<std::unique_ptr<IpcObjectImpl>> producerObjects;
136+
137+
for (unsigned i = 0; i < PRODUCER_COUNT; ++i)
138+
producerObjects.emplace_back(std::make_unique<IpcObjectImpl>(testPath));
139+
140+
IpcObjectImpl consumerObject(testPath);
141+
142+
constexpr unsigned messageCount = 500000;
143+
std::atomic_uint produced = 0;
144+
std::vector<unsigned> consumedData(messageCount);
145+
146+
consumerObject.sharedMemory.eventInit(&consumerObject.sharedMemory.getHeader()->receiverEvent);
147+
consumerObject.sharedMemory.eventInit(&consumerObject.sharedMemory.getHeader()->senderEvent);
148+
149+
std::vector<std::thread> threads;
150+
151+
const auto producer = [&](unsigned producerIdx) {
152+
auto& sharedMemory = producerObjects[producerIdx]->sharedMemory;
153+
auto& header = *sharedMemory.getHeader();
154+
155+
for (unsigned i = 0; i < messageCount; ++i)
156+
{
157+
SharedMutexGuard guard(&sharedMemory);
158+
159+
header.data = i;
160+
161+
// Problem - begin
162+
sharedMemory.eventFini(&header.senderEvent);
163+
if (sharedMemory.eventInit(&header.senderEvent) != FB_SUCCESS)
164+
(Arg::Gds(isc_random) << " eventInit(senderEvent) failed").raise();
165+
// Problem - end
166+
167+
const SLONG eventCounter = sharedMemory.eventClear(&header.senderEvent);
168+
169+
if (sharedMemory.eventPost(&header.receiverEvent) != FB_SUCCESS)
170+
(Arg::Gds(isc_random) << " eventPost failed").raise();
171+
172+
if (sharedMemory.eventWait(&header.senderEvent, eventCounter, 0) != FB_SUCCESS)
173+
(Arg::Gds(isc_random) << " eventWait failed").raise();
174+
175+
++produced;
176+
}
177+
};
178+
179+
const auto consumer = [&]() {
180+
auto& sharedMemory = consumerObject.sharedMemory;
181+
auto& header = *sharedMemory.getHeader();
182+
SLONG eventCounter = 0;
183+
184+
for (unsigned i = 0; i < messageCount * PRODUCER_COUNT; ++i)
185+
{
186+
if (sharedMemory.eventWait(&header.receiverEvent, eventCounter, 0) != FB_SUCCESS)
187+
(Arg::Gds(isc_random) << " eventWait failed").raise();
188+
189+
const auto data = header.data;
190+
++consumedData[data];
191+
192+
eventCounter = sharedMemory.eventClear(&header.receiverEvent);
193+
194+
if (sharedMemory.eventPost(&header.senderEvent) != FB_SUCCESS)
195+
(Arg::Gds(isc_random) << " eventPost failed").raise();
196+
}
197+
};
198+
199+
for (unsigned i = 0; i < PRODUCER_COUNT; ++i)
200+
threads.emplace_back(producer, i);
201+
202+
threads.emplace_back(consumer);
203+
204+
for (auto& thread : threads)
205+
thread.join();
206+
207+
BOOST_CHECK_EQUAL(produced.load(), messageCount * PRODUCER_COUNT);
208+
BOOST_CHECK_EQUAL(consumedData.size(), messageCount);
209+
210+
for (const auto& data : consumedData)
211+
BOOST_CHECK_EQUAL(data, 2);
212+
}
213+
214+
215+
BOOST_AUTO_TEST_SUITE_END() // SharedMemorySuite
216+
BOOST_AUTO_TEST_SUITE_END() // CommonSuite

0 commit comments

Comments
 (0)