Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{b612ef6f-bf32-494c-a5e8-3e6735d8543d}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="test.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>
4 changes: 4 additions & 0 deletions Tests/ModernThreadSafeLinkedStack.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.8" targetFramework="native" />
</packages>
5 changes: 5 additions & 0 deletions Tests/ModernThreadSafeLinkedStack.Tests/pch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//
// pch.cpp
//

#include "pch.h"
7 changes: 7 additions & 0 deletions Tests/ModernThreadSafeLinkedStack.Tests/pch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//
// pch.h
//

#pragma once

#include "gtest/gtest.h"
187 changes: 187 additions & 0 deletions Tests/ModernThreadSafeLinkedStack.Tests/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#include "pch.h"
#include <gtest/gtest.h>
#include <thread>
#include <vector>
#include <atomic>
#include <string>

#include "../../ThreadSafeStack/ModernThreadSafeLinkedStack.h"

/**
* @category Functional Tests (Single-threaded)
* @brief These tests verify the core LIFO logic and basic state management
* in a deterministic, single-threaded environment.
*/

// @test Verifies that a newly created stack is empty.
TEST(ModernThreadSafeLinkedStackTest, InitialStateIsCorrect) {
ModernThreadSafeLinkedStack<int> stack;
EXPECT_TRUE(stack.isEmpty());
}

// @test Validates basic push and pop operations, ensuring LIFO (Last-In, First-Out) order.
TEST(ModernThreadSafeLinkedStackTest, PushAndPopValues) {
ModernThreadSafeLinkedStack<int> stack;
stack.push(10);
stack.push(20);

EXPECT_FALSE(stack.isEmpty());

int val = 0;
// Check pop by reference (should be the last value pushed: 20)
bool success = stack.tryPop(val);
EXPECT_TRUE(success);
EXPECT_EQ(val, 20);

// Check pop by shared_ptr (should be the first value pushed: 10)
auto ptr = stack.tryPop();
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(*ptr, 10);

EXPECT_TRUE(stack.isEmpty());
}

// @test Tests tryPop variants for empty stack scenarios to ensure no crashes or exceptions.
TEST(ModernThreadSafeLinkedStackTest, TryPopEmptyStackReturnsNullOrFalse) {
ModernThreadSafeLinkedStack<int> stack;
int val = -1;

EXPECT_FALSE(stack.tryPop(val));
EXPECT_EQ(val, -1); // Value should remain unchanged
EXPECT_EQ(stack.tryPop(), nullptr);
}

// @test Confirms that clear() removes all elements and the stack becomes empty.
TEST(ModernThreadSafeLinkedStackTest, ClearEmptiesTheStack) {
ModernThreadSafeLinkedStack<std::string> stack;
stack.push("Alpha");
stack.push("Beta");
stack.push("Gamma");

stack.clear();
EXPECT_TRUE(stack.isEmpty());
EXPECT_EQ(stack.tryPop(), nullptr);
}

// @test Stress test for iterative cleanup logic.
// Verifies that a very deep stack does not cause a Stack Overflow on destruction.
TEST(ModernThreadSafeLinkedStackTest, HandlesLargeStacksWithoutStackOverflow) {
{
ModernThreadSafeLinkedStack<int> stack;
const int deepStackSize = 100000;
for (int i = 0; i < deepStackSize; ++i) {
stack.push(i);
}
// Stack goes out of scope here; destructor is called.
}
SUCCEED(); // If it didn't crash, the iterative cleanup works.
}

/**
* @category Concurrency Tests (Multi-threaded)
* @brief These tests verify thread safety under high contention to detect
* race conditions or data corruption.
*/

// @test Verifies thread safety during simultaneous push operations from multiple threads.
TEST(ModernThreadSafeLinkedStackTest, ConcurrentPushes) {
ModernThreadSafeLinkedStack<int> stack;
const int numThreads = 8;
const int pushesPerThread = 2000;
std::vector<std::thread> threads;

for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([&stack, pushesPerThread]() {
for (int j = 0; j < pushesPerThread; ++j) {
stack.push(j);
}
});
}

for (auto& t : threads) t.join();

// Verify all items were pushed by popping them and counting
int totalCount = 0;
int dummy;
while (stack.tryPop(dummy)) {
totalCount++;
}
EXPECT_EQ(totalCount, numThreads * pushesPerThread);
}

// @test Ensures no data loss occurs when multiple threads consume the stack simultaneously.
TEST(ModernThreadSafeLinkedStackTest, ConcurrentPops) {
ModernThreadSafeLinkedStack<int> stack;
const int totalElements = 10000;
for (int i = 0; i < totalElements; ++i) stack.push(i);

std::atomic<int> popCount{ 0 };
const int numThreads = 10;
std::vector<std::thread> threads;

for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([&stack, &popCount]() {
int val;
while (stack.tryPop(val)) {
popCount++;
}
});
}

for (auto& t : threads) t.join();

EXPECT_EQ(popCount.load(), totalElements);
EXPECT_TRUE(stack.isEmpty());
}

/**
* @test Comprehensive stress test for simultaneous producers and consumers.
* Verifies data integrity by comparing the sum of pushed values
* against the sum of popped values (checksum validation).
*/
TEST(ModernThreadSafeLinkedStackTest, ProducerConsumerDataIntegrity) {
ModernThreadSafeLinkedStack<int> stack;
const int numProducers = 4;
const int numConsumers = 4;
const int itemsPerProducer = 5000;

std::atomic<long long> sumPushed{ 0 };
std::atomic<long long> sumPopped{ 0 };
std::atomic<int> totalPoppedCount{ 0 };
std::atomic<bool> producersDone{ false };

// Producers push values and add to the total sum
auto producerFunc = [&]() {
for (int i = 1; i <= itemsPerProducer; ++i) {
stack.push(i);
sumPushed += i;
}
};

// Consumers pop values as long as producers are active or stack is not empty
auto consumerFunc = [&]() {
while (!producersDone.load() || !stack.isEmpty()) {
int val;
if (stack.tryPop(val)) {
sumPopped += val;
totalPoppedCount++;
}
else {
std::this_thread::yield(); // Minimal backoff
}
}
};

std::vector<std::thread> producers;
std::vector<std::thread> consumers;

for (int i = 0; i < numProducers; ++i) producers.emplace_back(producerFunc);
for (int i = 0; i < numConsumers; ++i) consumers.emplace_back(consumerFunc);

for (auto& p : producers) p.join();
producersDone = true; // Signal consumers to finish once stack is empty
for (auto& c : consumers) c.join();

EXPECT_EQ(totalPoppedCount.load(), numProducers * itemsPerProducer);
EXPECT_EQ(sumPushed.load(), sumPopped.load());
}
1 change: 1 addition & 0 deletions ThreadSafeStack.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="Tests/ModernThreadSafeLinkedStack.Tests/ModernThreadSafeLinkedStack.Tests.vcxproj" Id="b612ef6f-bf32-494c-a5e8-3e6735d8543d" />
<Project Path="Tests/ModernThreadSafeStack.Tests/ModernThreadSafeStack.Tests.vcxproj" Id="7f150933-516e-4c74-89b3-73754086ff32" />
<Project Path="Tests/SharedPtrWaitingThreadSafeStack.Tests/SharedPtrWaitingThreadSafeStack.Tests.vcxproj" Id="5f6662cb-9d21-4ce5-8716-77bdad8212a8" />
<Project Path="Tests/WaitingThreadSafeStack.Tests/WaitingThreadSafeStack.Tests.vcxproj" Id="763327fa-3786-480d-8bf6-856899243cd6" />
Expand Down
10 changes: 10 additions & 0 deletions ThreadSafeStack/ModernThreadSafeLinkedStack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "ModernThreadSafeLinkedStack.h"
#include <string>

// Explicit template instantiations for commonly used types.
// This forces the compiler to generate implementations for these types
// in this translation unit, which can reduce compilation times and
// prevent linker errors when the template is used across multiple files.
template class ModernThreadSafeLinkedStack<int>;
template class ModernThreadSafeLinkedStack<double>;
template class ModernThreadSafeLinkedStack<std::string>;
Loading