Skip to content
Open
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


# IDE
.idea/
.vs/
12 changes: 12 additions & 0 deletions solution/cpp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# CMake build directories
build/
cmake-build-*/
out/

# CMake cache and generated files
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
Makefile
*.cmake
!CMakeLists.txt
40 changes: 40 additions & 0 deletions solution/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.20)
project(AirlockProblem LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Add Debug build by default if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()

# Fetch GTest
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.tar.gz
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# Source files
set(SOURCES
src/CAirLock.cpp
src/CAstronaut.cpp
)

# Main executable
add_executable(airlock_main src/main.cpp ${SOURCES})
target_include_directories(airlock_main PRIVATE src)

# Tests
enable_testing()
add_executable(airlock_tests tests/tests.cpp ${SOURCES})
target_include_directories(airlock_tests PRIVATE src)
target_link_libraries(airlock_tests GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(airlock_tests)
5 changes: 5 additions & 0 deletions solution/cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# C++ Solution

This solution explores the way of using std::mutex and lock_guards.
It is a simple implementation of the airlock problem.
It uses the C++23 standard and follows [my coding style](https://gist.github.com/AbsintheScripting/4f2be73c91fc49fc6bc2cefbb2a52895#file-code_style-md).
161 changes: 161 additions & 0 deletions solution/cpp/src/CAirLock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include "CAirLock.h"
// STL headers
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>

std::string CAirLock::GetChamberStateName(const EChamberState chamber_state)
{
switch (chamber_state)
{
case EChamberState::EMPTY:
return "empty";
case EChamberState::PRESSURIZED:
return "pressurized";
case EChamberState::CHANGING_STATE:
return "cycling";
default:
return "unknown";
}
}

std::string CAirLock::GetDoorName(const EDoor door)
{
switch (door)
{
case EDoor::OUTSIDE:
return "outside";
case EDoor::INSIDE:
return "inside";
default:
return "unknown";
}
}

CAirLock::CAirLock()
: bIsOutsideDoorOpen(false),
bIsInsideDoorOpen(false),
chamberState(EChamberState::EMPTY),
bInsideChamberRequest(false)
{}

bool CAirLock::TryOpenDoor(const EDoor door_location)
{
bool bResult = false;
{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);

switch (door_location)
{
case EDoor::OUTSIDE:
if (chamberState == EChamberState::EMPTY && !bIsInsideDoorOpen)
{
bIsOutsideDoorOpen = true;
bInsideChamberRequest = false;
bResult = true;
}
break;
case EDoor::INSIDE:
if (chamberState == EChamberState::PRESSURIZED && !bIsOutsideDoorOpen)
{
bIsInsideDoorOpen = true;
bInsideChamberRequest = false;
bResult = true;
}
break;
}
} // CRITICAL SECTION END

return bResult;
}

void CAirLock::CloseDoor(const EDoor door_location)
{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);

switch (door_location)
{
case EDoor::OUTSIDE:
bIsOutsideDoorOpen = false;
break;
case EDoor::INSIDE:
bIsInsideDoorOpen = false;
break;
}
} // CRITICAL SECTION END

bool CAirLock::TryCycleChamber(const ECycleLocation cycle_location)
{
auto target_state = EChamberState::EMPTY;

{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);

if (bIsOutsideDoorOpen || bIsInsideDoorOpen)
{
return false;
}

// Logic:
// - If inside (CHAMBER), always allowed.
// - If outside (OUTSIDE/INSIDE in space/station), only allowed if no one requested cycle from inside the chamber.
if (cycle_location == ECycleLocation::OUTSIDE && bInsideChamberRequest)
{
return false;
}

if (chamberState == EChamberState::EMPTY)
{
target_state = EChamberState::PRESSURIZED;
}
else if (chamberState == EChamberState::PRESSURIZED)
{
target_state = EChamberState::EMPTY;
}
else
{
return false;
}

// Set inside request flag if cycle is from inside
if (cycle_location == ECycleLocation::INSIDE)
{
bInsideChamberRequest = true;
}

// chamber is cycling
std::cout << std::format("[AirLock] Chamber is cycling from {} to {} state.\n",
GetChamberStateName(chamberState), GetChamberStateName(target_state));
chamberState = EChamberState::CHANGING_STATE;
} // CRITICAL SECTION END

// Simulate pressure change
std::this_thread::sleep_for(std::chrono::milliseconds(100));

{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);
// chamber is ready
chamberState = target_state;
std::cout << std::format("[AirLock] Chamber cycled to {} state.\n",
GetChamberStateName(chamberState));
} // CRITICAL SECTION END

return true;
}

bool CAirLock::IsDoorOpen(const EDoor door_location) const
{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);
if (door_location == EDoor::OUTSIDE)
{
return bIsOutsideDoorOpen;
}
return bIsInsideDoorOpen;
} // CRITICAL SECTION END

CAirLock::EChamberState CAirLock::GetChamberState() const
{ // CRITICAL SECTION
std::lock_guard lock(mutexLock);
return chamberState;
} // CRITICAL SECTION END
59 changes: 59 additions & 0 deletions solution/cpp/src/CAirLock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once
// STL headers
#include <cstdint>
#include <mutex>
#include <string>

/**
* CAirLock
* Handles the airlock state and enforces safety rules.
*/
class CAirLock
{
public:
enum class EChamberState
{
EMPTY,
PRESSURIZED,
CHANGING_STATE
};

enum class EDoor
{
OUTSIDE,
INSIDE
};

enum class ECycleLocation
{
OUTSIDE, // Outside of the airlock (e.g., from space or station)
INSIDE // Inside the airlock chamber
};

static std::string GetChamberStateName(EChamberState chamber_state);
static std::string GetDoorName(EDoor door);

CAirLock();

[[nodiscard]]
bool TryOpenDoor(EDoor door_location);

void CloseDoor(EDoor door_location);

[[nodiscard]]
bool TryCycleChamber(ECycleLocation cycle_location);

[[nodiscard]]
bool IsDoorOpen(EDoor door_location) const;

[[nodiscard]]
EChamberState GetChamberState() const;

private:
mutable std::mutex mutexLock;

bool bIsOutsideDoorOpen;
bool bIsInsideDoorOpen;
EChamberState chamberState;
bool bInsideChamberRequest;
};
Loading