aboutsummaryrefslogtreecommitdiff
path: root/store/works/life/operating-system-experiment
diff options
context:
space:
mode:
Diffstat (limited to 'store/works/life/operating-system-experiment')
-rw-r--r--store/works/life/operating-system-experiment/.gitignore5
-rw-r--r--store/works/life/operating-system-experiment/Base.h18
-rw-r--r--store/works/life/operating-system-experiment/CMakeLists.txt37
-rw-r--r--store/works/life/operating-system-experiment/DataRaceDemo.cpp27
-rw-r--r--store/works/life/operating-system-experiment/DeadLockDetectionDemo.cpp113
-rw-r--r--store/works/life/operating-system-experiment/DeadLockTestData1.txt4
-rw-r--r--store/works/life/operating-system-experiment/DeadLockTestData2.txt4
-rw-r--r--store/works/life/operating-system-experiment/DeadLockTestData3.txt6
-rw-r--r--store/works/life/operating-system-experiment/DeadLockTestData4.txt9
-rw-r--r--store/works/life/operating-system-experiment/Interlocked.cpp16
-rw-r--r--store/works/life/operating-system-experiment/Interlocked.hpp10
-rw-r--r--store/works/life/operating-system-experiment/InterlockedAvoidDataRaceDemo.cpp28
-rw-r--r--store/works/life/operating-system-experiment/Mutex.cpp100
-rw-r--r--store/works/life/operating-system-experiment/Mutex.h45
-rw-r--r--store/works/life/operating-system-experiment/MutexAvoidDataRaceDemo.cpp42
-rw-r--r--store/works/life/operating-system-experiment/ParallelCalculationDemo.cpp75
-rw-r--r--store/works/life/operating-system-experiment/Semaphore.cpp98
-rw-r--r--store/works/life/operating-system-experiment/Semaphore.h47
-rw-r--r--store/works/life/operating-system-experiment/SemaphoreAvoidDataRaceDemo.cpp36
-rw-r--r--store/works/life/operating-system-experiment/Thread.cpp158
-rw-r--r--store/works/life/operating-system-experiment/Thread.h83
-rw-r--r--store/works/life/operating-system-experiment/main.cpp16
22 files changed, 977 insertions, 0 deletions
diff --git a/store/works/life/operating-system-experiment/.gitignore b/store/works/life/operating-system-experiment/.gitignore
new file mode 100644
index 0000000..502724c
--- /dev/null
+++ b/store/works/life/operating-system-experiment/.gitignore
@@ -0,0 +1,5 @@
+*.exe
+*.pdb
+.cache
+build
+compile_commands.json \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/Base.h b/store/works/life/operating-system-experiment/Base.h
new file mode 100644
index 0000000..6964c1c
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Base.h
@@ -0,0 +1,18 @@
+#ifndef HEADER_BASE_H
+#define HEADER_BASE_H
+
+#ifdef _WIN32
+#define CRU_WINDOWS 1
+#endif
+
+#ifdef CRU_WINDOWS
+#ifdef CRU_EXPORT_API
+#define CRU_API __declspec(dllexport)
+#else
+#define CRU_API __declspec(dllimport)
+#endif
+#else
+#define CRU_API
+#endif
+
+#endif \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/CMakeLists.txt b/store/works/life/operating-system-experiment/CMakeLists.txt
new file mode 100644
index 0000000..79fe786
--- /dev/null
+++ b/store/works/life/operating-system-experiment/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.20)
+
+set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake
+ CACHE STRING "Vcpkg toolchain file")
+
+project(operating-system-experiment)
+
+set(CMAKE_CXX_STANDARD 17)
+
+find_package(fmt CONFIG REQUIRED)
+find_package(Microsoft.GSL CONFIG REQUIRED)
+add_library(cru_system SHARED Thread.cpp Mutex.cpp Interlocked.cpp Semaphore.cpp)
+target_link_libraries(cru_system PUBLIC Microsoft.GSL::GSL fmt::fmt)
+target_compile_definitions(cru_system PUBLIC CRU_EXPORT_API)
+if(UNIX)
+target_link_libraries(cru_system PUBLIC pthread)
+endif()
+
+add_executable(main main.cpp)
+target_link_libraries(main PRIVATE cru_system)
+
+add_executable(data_race_demo DataRaceDemo.cpp)
+target_link_libraries(data_race_demo PRIVATE cru_system)
+
+add_executable(mutex_avoid_data_race_demo MutexAvoidDataRaceDemo.cpp)
+target_link_libraries(mutex_avoid_data_race_demo PRIVATE cru_system)
+
+add_executable(interlocked_avoid_data_race_demo InterlockedAvoidDataRaceDemo.cpp)
+target_link_libraries(interlocked_avoid_data_race_demo PRIVATE cru_system)
+
+add_executable(semaphore_avoid_data_race_demo SemaphoreAvoidDataRaceDemo.cpp)
+target_link_libraries(semaphore_avoid_data_race_demo PRIVATE cru_system)
+
+add_executable(parallel_calculation_demo ParallelCalculationDemo.cpp)
+target_link_libraries(parallel_calculation_demo PRIVATE cru_system)
+
+add_executable(dead_lock_detection_demo DeadLockDetectionDemo.cpp)
diff --git a/store/works/life/operating-system-experiment/DataRaceDemo.cpp b/store/works/life/operating-system-experiment/DataRaceDemo.cpp
new file mode 100644
index 0000000..a2f98d9
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DataRaceDemo.cpp
@@ -0,0 +1,27 @@
+#include "Thread.h"
+
+#include <iostream>
+
+int main() {
+ unsigned data = 0;
+
+ cru::Thread t1([&data] {
+ for (int i = 0; i < 100000; i++)
+ data += 10;
+ });
+
+ cru::Thread t2([&data] {
+ for (int i = 0; i < 100000; i++)
+ data += 10;
+ });
+
+ std::cout << "Created thread: " << t1.GetNativeID() << '\n';
+ std::cout << "Created thread: " << t2.GetNativeID() << '\n';
+
+ t1.Join();
+ t2.Join();
+ std::cout << "Answer is " << data << ", which is "
+ << (data == 2000000 ? "correct" : "false") << '\n';
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/DeadLockDetectionDemo.cpp b/store/works/life/operating-system-experiment/DeadLockDetectionDemo.cpp
new file mode 100644
index 0000000..bea2b43
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DeadLockDetectionDemo.cpp
@@ -0,0 +1,113 @@
+// Demo使用方法:
+// 本程序采用标准C++11代码写成,请使用支持C++11的编译器编译。
+//
+// 程序接受的输入为n个整数对,其中1-10000表示进程,10001及以上表示锁,顺序很重要:
+// 1 10001 -> process 1 is acquiring or waiting for lock 1
+// 10001 1 -> lock 1 is owned by process 1
+//
+// 建议采用重定向输入至文件的方式输入,提供4个典型情况的测试数据。
+//
+
+#include <iostream>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+const int LOCK_START_INDEX = 10000;
+
+struct Node {
+ int id;
+};
+
+bool operator==(const Node &left, const Node &right) {
+ return left.id == right.id;
+}
+
+template <> struct std::hash<Node> {
+ std::size_t operator()(const Node &node) const {
+ return std::hash<int>{}(node.id);
+ }
+};
+
+std::ostream &operator<<(std::ostream &left, const Node &right) {
+ left << (right.id > LOCK_START_INDEX ? "lock" : "process") << ' '
+ << (right.id > LOCK_START_INDEX ? right.id - LOCK_START_INDEX
+ : right.id);
+ return left;
+}
+
+bool dfs(
+ const Node *root,
+ std::unordered_map<const Node *, std::unordered_set<const Node *>> &edges,
+ std::unordered_map<const Node *, bool> &visited) {
+
+ if (visited[root]) {
+ std::cout << "Ohhhhhh, it is already visited!\n";
+ return true;
+ }
+
+ visited[root] = true;
+ auto r = edges.find(root);
+ if (r != edges.cend())
+ for (auto n : r->second) {
+ std::cout << "from " << *root << " go to " << *n << "\n";
+ if (dfs(n, edges, visited))
+ return true;
+ std::cout << "from " << *n << " back to " << *root << "\n";
+ }
+
+ edges.erase(root);
+ return false;
+}
+
+int main() {
+ std::vector<int> ns;
+
+ while (std::cin) {
+ int n;
+ std::cin >> n;
+ if (std::cin)
+ ns.push_back(n);
+ }
+
+ if (!std::cin.eof()) {
+ std::cerr << "Failed to parse input.\n";
+ return -1;
+ }
+
+ if (ns.size() % 2 != 0) {
+ std::cerr << "Input integer number must be even.\n";
+ return -1;
+ }
+
+ std::unordered_set<Node> nodes;
+ std::unordered_map<const Node *, std::unordered_set<const Node *>> edges;
+
+ for (int i = 0; i < ns.size(); i += 2) {
+ int p = ns[i];
+ int l = ns[i + 1];
+ auto r1 = nodes.insert({p});
+ auto r2 = nodes.insert({l});
+ edges[&(*r1.first)].insert(&(*r2.first));
+ }
+
+ bool h = false;
+
+ while (!edges.empty()) {
+ auto n = edges.cbegin()->first;
+ std::unordered_map<const Node *, bool> visited;
+ std::cout << "Begin to detect child graph containing '" << *n << "'.\n";
+ if (dfs(n, edges, visited)) {
+ h = true;
+ break;
+ }
+ }
+
+ if (h) {
+ std::cout << "Cycle, aka dead lock, detected.\n";
+ } else {
+ std::cout << "You are free of dead lock!!!\n";
+ }
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/DeadLockTestData1.txt b/store/works/life/operating-system-experiment/DeadLockTestData1.txt
new file mode 100644
index 0000000..0c8dd14
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DeadLockTestData1.txt
@@ -0,0 +1,4 @@
+1 10001
+2 10001
+3 10001
+4 10001
diff --git a/store/works/life/operating-system-experiment/DeadLockTestData2.txt b/store/works/life/operating-system-experiment/DeadLockTestData2.txt
new file mode 100644
index 0000000..d632f2a
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DeadLockTestData2.txt
@@ -0,0 +1,4 @@
+10001 1
+1 10002
+2 10001
+10002 2
diff --git a/store/works/life/operating-system-experiment/DeadLockTestData3.txt b/store/works/life/operating-system-experiment/DeadLockTestData3.txt
new file mode 100644
index 0000000..1e1692d
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DeadLockTestData3.txt
@@ -0,0 +1,6 @@
+1 10001
+10001 2
+2 10002
+10002 3
+3 10003
+10003 1
diff --git a/store/works/life/operating-system-experiment/DeadLockTestData4.txt b/store/works/life/operating-system-experiment/DeadLockTestData4.txt
new file mode 100644
index 0000000..d124102
--- /dev/null
+++ b/store/works/life/operating-system-experiment/DeadLockTestData4.txt
@@ -0,0 +1,9 @@
+1 10001
+2 10001
+3 10002
+3 10004
+10001 3
+4 10001
+4 10005
+10004 5
+10005 5
diff --git a/store/works/life/operating-system-experiment/Interlocked.cpp b/store/works/life/operating-system-experiment/Interlocked.cpp
new file mode 100644
index 0000000..7fc8c6b
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Interlocked.cpp
@@ -0,0 +1,16 @@
+#include "Interlocked.hpp"
+
+#ifdef CRU_WINDOWS
+#include <Windows.h>
+#else
+#endif
+
+namespace cru {
+void CruInterlockedAdd(volatile long long *v, long long a) {
+#ifdef CRU_WINDOWS
+ InterlockedAdd64(v, a);
+#else
+ __sync_fetch_and_add(v, a);
+#endif
+}
+} // namespace cru
diff --git a/store/works/life/operating-system-experiment/Interlocked.hpp b/store/works/life/operating-system-experiment/Interlocked.hpp
new file mode 100644
index 0000000..7e09b60
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Interlocked.hpp
@@ -0,0 +1,10 @@
+#ifndef HRADER_INTERLOCKED_H
+#define HRADER_INTERLOCKED_H
+
+#include "Base.h"
+
+namespace cru {
+CRU_API void CruInterlockedAdd(volatile long long *v, long long a);
+}
+
+#endif
diff --git a/store/works/life/operating-system-experiment/InterlockedAvoidDataRaceDemo.cpp b/store/works/life/operating-system-experiment/InterlockedAvoidDataRaceDemo.cpp
new file mode 100644
index 0000000..91d6d50
--- /dev/null
+++ b/store/works/life/operating-system-experiment/InterlockedAvoidDataRaceDemo.cpp
@@ -0,0 +1,28 @@
+#include "Interlocked.hpp"
+#include "Thread.h"
+
+#include <iostream>
+
+int main() {
+ volatile long long data = 0;
+
+ cru::Thread t1([&data] {
+ for (int i = 0; i < 100000; i++)
+ cru::CruInterlockedAdd(&data, 10);
+ });
+
+ cru::Thread t2([&data] {
+ for (int i = 0; i < 100000; i++)
+ cru::CruInterlockedAdd(&data, 10);
+ });
+
+ std::cout << "Created thread: " << t1.GetNativeID() << '\n';
+ std::cout << "Created thread: " << t2.GetNativeID() << '\n';
+
+ t1.Join();
+ t2.Join();
+ std::cout << "Answer is " << data << ", which is "
+ << (data == 2000000 ? "correct" : "false") << '\n';
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/Mutex.cpp b/store/works/life/operating-system-experiment/Mutex.cpp
new file mode 100644
index 0000000..8acfdc5
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Mutex.cpp
@@ -0,0 +1,100 @@
+#include "Mutex.h"
+
+#include <cassert>
+
+#ifndef CRU_WINDOWS
+#include <errno.h>
+#endif
+
+namespace cru {
+Mutex::Mutex() {
+#ifdef CRU_WINDOWS
+ handle_ = CreateMutexW(nullptr, FALSE, nullptr);
+ assert(handle_);
+#else
+ mutex_ = std::make_unique<pthread_mutex_t>();
+
+ auto c = pthread_mutex_init(mutex_.get(), nullptr);
+ assert(c == 0);
+#endif
+}
+
+Mutex::Mutex(Mutex &&other)
+#ifdef CRU_WINDOWS
+ : handle_(other.handle_)
+#else
+ : mutex_(std::move(other.mutex_))
+#endif
+{
+#ifdef CRU_WINDOWS
+ other.handle_ = nullptr;
+#endif
+}
+
+Mutex &Mutex::operator=(Mutex &&other) {
+ if (this != &other) {
+ Destroy();
+#ifdef CRU_WINDOWS
+ handle_ = other.handle_;
+ other.handle_ = nullptr;
+#else
+ mutex_ = std::move(other.mutex_);
+#endif
+ }
+ return *this;
+}
+
+Mutex::~Mutex() { Destroy(); }
+
+void Mutex::Lock() {
+#ifdef CRU_WINDOWS
+ auto c = WaitForSingleObject(handle_, INFINITE);
+ assert(c == WAIT_OBJECT_0);
+#else
+ assert(mutex_);
+ auto c = pthread_mutex_lock(mutex_.get());
+ assert(c == 0);
+#endif
+}
+
+bool Mutex::TryLock() {
+#ifdef CRU_WINDOWS
+ auto c = WaitForSingleObject(handle_, 0);
+ assert(c == WAIT_OBJECT_0 || c == WAIT_TIMEOUT);
+ return c == WAIT_OBJECT_0 ? true : false;
+#else
+ assert(mutex_);
+ auto c = pthread_mutex_trylock(mutex_.get());
+ assert(c == 0 || c == EBUSY);
+ return c == 0 ? true : false;
+#endif
+}
+
+void Mutex::Unlock() {
+#ifdef CRU_WINDOWS
+ auto c = ReleaseMutex(handle_);
+ assert(c);
+#else
+ assert(mutex_);
+ auto c = pthread_mutex_unlock(mutex_.get());
+ assert(c == 0);
+#endif
+}
+
+void Mutex::Destroy() {
+#ifdef CRU_WINDOWS
+ if (handle_ != nullptr) {
+ auto c = CloseHandle(handle_);
+ assert(c);
+ handle_ = nullptr;
+ }
+#else
+ if (mutex_ != nullptr) {
+ auto c = pthread_mutex_destroy(mutex_.get());
+ assert(c == 0);
+ mutex_ = nullptr;
+ }
+#endif
+}
+
+} // namespace cru
diff --git a/store/works/life/operating-system-experiment/Mutex.h b/store/works/life/operating-system-experiment/Mutex.h
new file mode 100644
index 0000000..d561f1a
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Mutex.h
@@ -0,0 +1,45 @@
+#ifndef HEADER_MUTEX_H
+#define HEADER_MUTEX_H
+
+#include "Base.h"
+
+#include <memory>
+
+#ifdef CRU_WINDOWS
+#include <Windows.h>
+#else
+#include <pthread.h>
+#endif
+
+namespace cru {
+class CRU_API Mutex {
+public:
+ Mutex();
+
+ Mutex(const Mutex &other) = delete;
+ Mutex &operator=(const Mutex &other) = delete;
+
+ Mutex(Mutex &&other);
+ Mutex &operator=(Mutex &&other);
+
+ ~Mutex();
+
+public:
+ void Lock();
+ bool TryLock();
+
+ void Unlock();
+
+private:
+ void Destroy();
+
+private:
+#ifdef CRU_WINDOWS
+ HANDLE handle_;
+#else
+ std::unique_ptr<pthread_mutex_t> mutex_;
+#endif
+};
+} // namespace cru
+
+#endif \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/MutexAvoidDataRaceDemo.cpp b/store/works/life/operating-system-experiment/MutexAvoidDataRaceDemo.cpp
new file mode 100644
index 0000000..81a7aa1
--- /dev/null
+++ b/store/works/life/operating-system-experiment/MutexAvoidDataRaceDemo.cpp
@@ -0,0 +1,42 @@
+#include "Mutex.h"
+#include "Thread.h"
+
+#include <iostream>
+
+int main() {
+
+ unsigned data = 0;
+ cru::Mutex mutex;
+ mutex.Lock();
+
+ cru::Thread t1([&data, &mutex] {
+ for (int i = 0; i < 100000; i++) {
+ mutex.Lock();
+ data += 10;
+ //std::cout << "Data is now: " << data << '\n';
+ mutex.Unlock();
+ }
+ });
+
+ cru::Thread t2([&data, &mutex] {
+ for (int i = 0; i < 100000; i++) {
+ mutex.Lock();
+ data += 10;
+ //std::cout << "Data is now: " << data << '\n';
+ mutex.Unlock();
+ }
+ });
+
+ std::cout << "Created thread: " << t1.GetNativeID() << '\n';
+ std::cout << "Created thread: " << t2.GetNativeID() << '\n';
+
+ mutex.Unlock();
+
+ t1.Join();
+ t2.Join();
+
+ std::cout << "Answer is " << data << ", which is "
+ << (data == 2000000 ? "correct" : "false") << '\n';
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/ParallelCalculationDemo.cpp b/store/works/life/operating-system-experiment/ParallelCalculationDemo.cpp
new file mode 100644
index 0000000..0174c55
--- /dev/null
+++ b/store/works/life/operating-system-experiment/ParallelCalculationDemo.cpp
@@ -0,0 +1,75 @@
+#include "Thread.h"
+
+#include <iostream>
+
+const long long N = 1e9;
+
+int main(int argc, char **argv) {
+ int thread_number;
+ if (argc == 1) {
+ thread_number = 1;
+ } else if (argc == 2) {
+ thread_number = std::atoi(argv[1]);
+ if (thread_number <= 0) {
+ std::cerr << "Argument must be a positive integer.\n";
+ return -1;
+ }
+ } else {
+ std::cerr << "Too many arguments.\n";
+ return -1;
+ }
+
+ std::cout << "Use " << thread_number << " threads to calculate sum of 1-" << N
+ << ".\n";
+
+ if (thread_number == 1) {
+ long long sum = 0;
+ for (long long i = 1; i <= 1e9; i++) {
+ sum += i;
+ }
+ std::cout << "Sum of 1-" << N << " is " << sum << '\n';
+ } else {
+ std::vector<cru::Thread> threads(thread_number);
+ std::vector<long long> partial_sum(thread_number);
+
+ long long step = N / thread_number;
+
+ for (int i = 0; i < thread_number; i++) {
+ long long start = step * i;
+ long long end = step * (i + 1);
+
+ long long &ps = partial_sum[i];
+
+ if (i == thread_number - 1) {
+ threads[i] = cru::Thread([&ps, start] {
+ long long sum = 0;
+ for (long long j = start; j <= N; j++) {
+ sum += j;
+ }
+ ps = sum;
+ });
+ } else {
+ threads[i] = cru::Thread([&ps, start, end] {
+ long long sum = 0;
+ for (int j = start; j < end; j++) {
+ sum += j;
+ }
+ ps = sum;
+ });
+ }
+ }
+
+ for (auto &thread : threads) {
+ thread.Join();
+ }
+
+ long long sum = 0;
+ for (auto ps : partial_sum) {
+ sum += ps;
+ }
+
+ std::cout << "Sum of 1-" << N << " is " << sum << '\n';
+ }
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/Semaphore.cpp b/store/works/life/operating-system-experiment/Semaphore.cpp
new file mode 100644
index 0000000..aceef4d
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Semaphore.cpp
@@ -0,0 +1,98 @@
+#include "Semaphore.h"
+
+#include <cassert>
+
+#ifndef CRU_WINDOWS
+#include <errno.h>
+#endif
+
+namespace cru {
+Semaphore::Semaphore(unsigned init_value) {
+#ifdef CRU_WINDOWS
+ handle_ = CreateSemaphoreW(nullptr, init_value, 100, nullptr);
+ assert(handle_);
+#else
+ semaphore_ = std::make_unique<sem_t>();
+ auto c = sem_init(semaphore_.get(), 0, init_value);
+ assert(c == 0);
+#endif
+}
+
+Semaphore::Semaphore(Semaphore &&other)
+#ifdef CRU_WINDOWS
+ : handle_(other.handle_)
+#else
+ : semaphore_(std::move(other.semaphore_))
+#endif
+{
+#ifdef CRU_WINDOWS
+ other.handle_ = nullptr;
+#endif
+}
+
+Semaphore &Semaphore::operator=(Semaphore &&other) {
+ if (this != &other) {
+ Destroy();
+#ifdef CRU_WINDOWS
+ handle_ = other.handle_;
+ other.handle_ = nullptr;
+#else
+ semaphore_ = std::move(other.semaphore_);
+#endif
+ }
+ return *this;
+}
+
+Semaphore::~Semaphore() { Destroy(); }
+
+void Semaphore::P() { Acquire(); }
+void Semaphore::V() { Release(); }
+
+void Semaphore::Acquire() {
+#ifdef CRU_WINDOWS
+ auto c = WaitForSingleObject(handle_, INFINITE);
+ assert(c == WAIT_OBJECT_0);
+#else
+ auto c = sem_wait(semaphore_.get());
+ assert(c == 0);
+#endif
+}
+
+bool Semaphore::TryAcquire() {
+#ifdef CRU_WINDOWS
+ auto c = WaitForSingleObject(handle_, 0);
+ assert(c == WAIT_OBJECT_0 || c == WAIT_TIMEOUT);
+ return c == WAIT_OBJECT_0 ? true : false;
+#else
+ auto c = sem_trywait(semaphore_.get());
+ assert((c == 0) || (c == -1 && errno == EAGAIN));
+ return c == 0 ? true : false;
+#endif
+}
+
+void Semaphore::Release() {
+#ifdef CRU_WINDOWS
+ auto c = ReleaseSemaphore(handle_, 1, nullptr);
+ assert(c);
+#else
+ auto c = sem_post(semaphore_.get());
+ assert(c == 0);
+#endif
+}
+
+void Semaphore::Destroy() {
+#ifdef CRU_WINDOWS
+ if (handle_ != nullptr) {
+ auto c = CloseHandle(handle_);
+ assert(c);
+ handle_ = nullptr;
+ }
+#else
+ if (semaphore_ != nullptr) {
+ auto c = sem_destroy(semaphore_.get());
+ assert(c == 0);
+ semaphore_ = nullptr;
+ }
+#endif
+}
+} // namespace cru \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/Semaphore.h b/store/works/life/operating-system-experiment/Semaphore.h
new file mode 100644
index 0000000..430c036
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Semaphore.h
@@ -0,0 +1,47 @@
+#ifndef HEADER_SEMAPHORE_H
+#define HEADER_SEMAPHORE_H
+
+#include "Base.h"
+
+#include <memory>
+
+#ifdef CRU_WINDOWS
+#include <Windows.h>
+#else
+#include <semaphore.h>
+#endif
+
+namespace cru {
+class CRU_API Semaphore {
+public:
+ explicit Semaphore(unsigned init_value = 1);
+
+ Semaphore(const Semaphore &other) = delete;
+ Semaphore &operator=(const Semaphore &other) = delete;
+
+ Semaphore(Semaphore &&other);
+ Semaphore &operator=(Semaphore &&other);
+
+ ~Semaphore();
+
+public:
+ void P();
+ void V();
+
+ void Acquire();
+ bool TryAcquire();
+ void Release();
+
+private:
+ void Destroy();
+
+private:
+#ifdef CRU_WINDOWS
+ HANDLE handle_ = nullptr;
+#else
+ std::unique_ptr<sem_t> semaphore_;
+#endif
+};
+} // namespace cru
+
+#endif \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/SemaphoreAvoidDataRaceDemo.cpp b/store/works/life/operating-system-experiment/SemaphoreAvoidDataRaceDemo.cpp
new file mode 100644
index 0000000..0068082
--- /dev/null
+++ b/store/works/life/operating-system-experiment/SemaphoreAvoidDataRaceDemo.cpp
@@ -0,0 +1,36 @@
+#include "Semaphore.h"
+#include "Thread.h"
+
+#include <iostream>
+
+int main() {
+ unsigned data = 0;
+
+ cru::Semaphore semaphore;
+
+ cru::Thread t1([&data, &semaphore] {
+ for (int i = 0; i < 100000; i++) {
+ semaphore.P();
+ data += 10;
+ semaphore.V();
+ }
+ });
+
+ cru::Thread t2([&data, &semaphore] {
+ for (int i = 0; i < 100000; i++) {
+ semaphore.P();
+ data += 10;
+ semaphore.V();
+ }
+ });
+
+ std::cout << "Created thread: " << t1.GetNativeID() << '\n';
+ std::cout << "Created thread: " << t2.GetNativeID() << '\n';
+
+ t1.Join();
+ t2.Join();
+ std::cout << "Answer is " << data << ", which is "
+ << (data == 2000000 ? "correct" : "false") << '\n';
+
+ return 0;
+}
diff --git a/store/works/life/operating-system-experiment/Thread.cpp b/store/works/life/operating-system-experiment/Thread.cpp
new file mode 100644
index 0000000..0bc4c18
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Thread.cpp
@@ -0,0 +1,158 @@
+#include "Thread.h"
+
+#include <cassert>
+#include <exception>
+#include <utility>
+
+namespace cru {
+Thread::Thread(Thread &&other) noexcept
+ : detached_(other.detached_), joined_(other.joined_),
+#ifdef CRU_WINDOWS
+ thread_handle_(other.thread_handle_)
+#else
+ thread_(std::move(other.thread_))
+#endif
+{
+ other.joined_ = false;
+#ifdef CRU_WINDOWS
+ other.thread_handle_ = nullptr;
+#endif
+} // namespace cru
+
+Thread &Thread::operator=(Thread &&other) noexcept {
+ if (this != &other) {
+ detached_ = other.detached_;
+ joined_ = other.joined_;
+#ifdef CRU_WINDOWS
+ thread_handle_ = other.thread_handle_;
+ other.thread_handle_ = nullptr;
+#else
+ thread_ = std::move(other.thread_);
+#endif
+ other.detached_ = false;
+ other.joined_ = false;
+ }
+
+ return *this;
+}
+
+Thread::~Thread() { Destroy(); }
+
+void Thread::Join() {
+ joined_ = true;
+#ifdef CRU_WINDOWS
+ assert(thread_handle_);
+ WaitForSingleObject(thread_handle_, INFINITE);
+#else
+ assert(thread_);
+ auto c = pthread_join(*thread_, nullptr);
+ assert(c == 0);
+#endif
+}
+
+void Thread::Detach() {
+#ifdef CRU_WINDOWS
+ assert(thread_handle_);
+#else
+ assert(thread_);
+#endif
+ detached_ = true;
+}
+
+#ifdef CRU_WINDOWS
+DWORD
+#else
+pthread_t
+#endif
+Thread::GetNativeID() {
+#ifdef CRU_WINDOWS
+ assert(thread_handle_);
+ return thread_id_;
+#else
+ assert(thread_);
+ return *thread_;
+#endif
+}
+
+#ifdef CRU_WINDOWS
+HANDLE
+#else
+pthread_t
+#endif
+Thread::GetNativeHandle() {
+#ifdef CRU_WINDOWS
+ assert(thread_handle_);
+ return thread_handle_;
+#else
+ assert(thread_);
+ return *thread_;
+#endif
+}
+
+void Thread::swap(Thread &other) noexcept {
+#ifdef CRU_WINDOWS
+ Thread temp = std::move(*this);
+ *this = std::move(other);
+ other = std::move(temp);
+#else
+#endif
+}
+
+void Thread::Destroy() noexcept {
+ if (!detached_ && !joined_ &&
+#ifdef CRU_WINDOWS
+ thread_handle_ != nullptr
+#else
+ thread_ != nullptr
+#endif
+ ) {
+ std::terminate();
+ } else {
+ detached_ = false;
+ joined_ = false;
+#ifdef CRU_WINDOWS
+ thread_id_ = 0;
+ if (thread_handle_ != nullptr) {
+ auto c = CloseHandle(thread_handle_);
+ assert(c);
+ thread_handle_ = nullptr;
+ }
+#else
+ thread_ = nullptr;
+#endif
+ }
+}
+
+namespace {
+#ifdef CRU_WINDOWS
+DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) {
+ auto p = static_cast<std::function<void()> *>(lpParameter);
+ (*p)();
+ delete p;
+ return 0;
+}
+#else
+void *ThreadProc(void *data) {
+ auto p = static_cast<std::function<void()> *>(data);
+ (*p)();
+ delete p;
+ return nullptr;
+}
+
+#endif
+} // namespace
+
+void Thread::CreateThread(std::function<void()> *proc) {
+#ifdef CRU_WINDOWS
+ thread_handle_ = ::CreateThread(nullptr, 0, ThreadProc,
+ static_cast<void *>(proc), 0, &thread_id_);
+ assert(thread_handle_);
+#else
+ thread_.reset(new pthread_t());
+ auto c = pthread_create(thread_.get(), nullptr, ThreadProc,
+ static_cast<void *>(proc));
+ assert(c == 0);
+#endif
+};
+
+} // namespace cru \ No newline at end of file
diff --git a/store/works/life/operating-system-experiment/Thread.h b/store/works/life/operating-system-experiment/Thread.h
new file mode 100644
index 0000000..4ad1ef4
--- /dev/null
+++ b/store/works/life/operating-system-experiment/Thread.h
@@ -0,0 +1,83 @@
+#ifndef HEADER_THREAD_H
+#define HEADER_THREAD_H
+
+#include "Base.h"
+
+#ifdef CRU_WINDOWS
+#include <Windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#ifdef __cplusplus
+
+#include <cassert>
+#include <functional>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace cru {
+class CRU_API Thread {
+public:
+ Thread() = default;
+ template <typename Fn, typename... Args> Thread(Fn &&process, Args &&...args);
+ Thread(const Thread &other) = delete;
+ Thread(Thread &&other) noexcept;
+ Thread &operator=(const Thread &other) = delete;
+ Thread &operator=(Thread &&other) noexcept;
+ ~Thread();
+
+public:
+ void Join();
+ void Detach();
+
+#ifdef CRU_WINDOWS
+ DWORD
+#else
+ pthread_t
+#endif
+ GetNativeID();
+
+#ifdef CRU_WINDOWS
+ HANDLE
+#else
+ pthread_t
+#endif
+ GetNativeHandle();
+
+ void swap(Thread &other) noexcept;
+
+private:
+ void Destroy() noexcept;
+ void CreateThread(std::function<void()> *proc);
+
+private:
+ bool detached_ = false;
+ bool joined_ = false;
+
+#ifdef CRU_WINDOWS
+ DWORD thread_id_ = 0;
+ HANDLE thread_handle_ = nullptr;
+#else
+ std::unique_ptr<pthread_t> thread_;
+#endif
+};
+
+template <typename Fn, typename... Args>
+Thread::Thread(Fn &&process, Args &&...args) {
+ std::tuple<std::decay_t<Args>...> a{std::forward<Args>(args)...};
+ auto p = new std::function<void()>(
+ [process = std::forward<Fn>(process), args = std::move(a)]() {
+ std::apply(process, std::move(args));
+ });
+
+ CreateThread(p);
+};
+} // namespace cru
+
+#endif
+
+#endif
diff --git a/store/works/life/operating-system-experiment/main.cpp b/store/works/life/operating-system-experiment/main.cpp
new file mode 100644
index 0000000..ada8c85
--- /dev/null
+++ b/store/works/life/operating-system-experiment/main.cpp
@@ -0,0 +1,16 @@
+#include "Thread.h"
+
+#include <iostream>
+#include <string>
+
+int main() {
+ cru::Thread thread1([](const std::string &s) { std::cout << s; },
+ "Hello world! 1\n");
+ thread1.Join();
+
+ cru::Thread thread2([](const std::string &s) { std::cout << s; },
+ "Hello world! 2\n");
+ thread2.Join();
+
+ return 0;
+}