├── .bazelrc ├── .clang-format ├── .gitignore ├── .vscode ├── cpp_properties.json ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── MODULE.bazel ├── MODULE.bazel.lock ├── README.md ├── a.out ├── cpp_multithreading_timeline_wavy.png ├── lambda_jthread_main ├── main ├── src ├── lib │ ├── BUILD │ ├── logger.h │ ├── range_accumulator.h │ ├── utility.h │ └── vector_accumulator.h └── main │ ├── BUILD │ ├── async_lambda_main.cc │ ├── async_main.cc │ ├── atomic │ ├── BUILD │ ├── atomic_demo_main.cc │ ├── compare_exchange_main.cc │ ├── fedoro_main.cc │ ├── fetch_main.cc │ ├── lock_free_main.cc │ ├── plot.py │ ├── range_accumulator_benchmark.cc │ ├── range_accumulator_benchmark.csv │ ├── vector_accumulator_benchmark.cc │ └── vector_accumulator_benchmark.csv │ ├── conditional_variable │ ├── BUILD │ ├── producer_consumer_conditional_var_main.cc │ ├── producer_consumer_conditional_var_simple.cc │ └── producer_consumer_lock_main.cc │ ├── exceptions │ ├── BUILD │ ├── basic_exception.cc │ ├── central_queue.cc │ ├── exception_ptr.cc │ ├── exception_ptr_vector.cpp │ ├── future_promise_jthread.cc │ ├── future_promise_thread.cc │ ├── jthread_exception.cc │ ├── raii.py │ ├── safe_locks.cc │ ├── thread_handling_exception.cc │ └── thread_not_handling_exception.cc │ ├── functors_main.cc │ ├── functors_main_unique_ptr.cc │ ├── jthread │ ├── BUILD │ ├── bad_thread1.cc │ ├── bad_thread2.cc │ ├── bad_thread_stop_1.cc │ ├── bad_thread_stop_2.cc │ ├── bad_thread_stop_3.cc │ ├── bad_thread_stop_with_callback.cc │ ├── better_jthread_stop_with_callback.cc │ ├── better_jthread_stop_with_callback2.cc │ ├── cppThreadEvolution.py │ ├── good_jthread1.cc │ ├── good_jthread2.cc │ ├── good_jthread_stop_1.cc │ ├── good_jthread_stop_2.cc │ ├── good_jthread_stop_with_callback.cc │ ├── jthread.cc │ ├── lambda_jthread_main.cc │ └── pitfalls.cc │ ├── lambda_main.cc │ ├── main.cc │ ├── map_reduce │ ├── BUILD │ ├── count_words.cc │ ├── count_words_file.cc │ ├── khayam.txt │ ├── lib │ │ ├── BUILD │ │ ├── map_reduce.h │ │ └── thread_safe_map.h │ ├── masnavi.txt │ ├── shakespear.txt │ └── tests │ │ ├── BUILD │ │ └── map_reduce_test.cc │ ├── memory_order │ ├── BUILD │ ├── aquire_release_main.cc │ ├── memory_order_counter_main copy 2.cc │ ├── memory_order_counter_main.cc │ ├── memory_order_main.cc │ ├── relaxed_4_threads_main.cc │ ├── relaxed_main.cc │ ├── relaxed_main_two_threads.cc │ ├── release_acquire_3t_main.cc │ ├── release_acquire_main.cc │ ├── release_acquire_main_simple.cc │ ├── seq_cst_main.cc │ ├── sequential_consistency_atomic_main.cc │ └── sequential_consistency_main.cc │ ├── mutex │ ├── BUILD │ ├── lock_guard_main.cc │ ├── lock_guard_multiple_mutex_main.cc │ ├── lock_unlock_main.cc │ ├── mutex_seq_cst_main.cc │ ├── no_lock_main.cc │ ├── race_condition_main.cc │ ├── shared_mutex_main.cc │ ├── test_main.cc │ └── unique_lock_main.cc │ ├── number_of_cores.cc │ └── vector_of_threads_main.cc ├── tests ├── BUILD ├── range_accumulator_test.cc ├── solution_test.cc └── vector_accumulator_test.cc └── youtube.png /.bazelrc: -------------------------------------------------------------------------------- 1 | # Address sanitizer config 2 | # Use with bazel run --config=asan or lsan 3 | build:asan --strip=never 4 | build:asan --copt -fsanitize=address 5 | build:asan --copt -DADDRESS_SANITIZER 6 | build:asan --copt -O0 7 | build:asan --copt -g 8 | build:asan --copt -fno-omit-frame-pointer 9 | build:asan --linkopt -fsanitize=address 10 | build:asan --sandbox_debug 11 | build:asan --spawn_strategy=standalone 12 | build --host_cxxopt='-std=c++23' 13 | 14 | # Memory sanitizer config 15 | build:msan --strip=never 16 | build:msan --copt -fsanitize=memory 17 | build:msan --copt -O0 18 | build:msan --copt -g 19 | build:msan --linkopt -fsanitize=memory 20 | build:msan --sandbox_debug 21 | build:msan --spawn_strategy=standalone 22 | 23 | # Thread sanitizer config 24 | build:tsan --copt -fsanitize=thread 25 | 26 | build --cxxopt='-std=c++23' 27 | build --cxxopt='-stdlib=libc++' 28 | build --cxxopt='-fexperimental-library' 29 | build --host_cxxopt='-std=c++23' 30 | build --host_cxxopt='-stdlib=libc++' 31 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 2 3 | AlwaysBreakTemplateDeclarations: Yes -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bazel-Bazel_with_GTest 2 | /bazel-bin 3 | /bazel-genfiles 4 | /bazel-out 5 | /bazel-testlogs 6 | /bazel-template 7 | /bazel-* 8 | -------------------------------------------------------------------------------- /.vscode/cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1" 8 | ], 9 | "defines": [], 10 | "compilerPath": "/usr/bin/clang++", 11 | "cStandard": "c17", 12 | "cppStandard": "c++20", 13 | "intelliSenseMode": "macos-clang-x64", 14 | "compilerArgs": [ 15 | "-stdlib=libc++", 16 | "-std=c++20", 17 | "-fexperimental-library" 18 | ] 19 | } 20 | ], 21 | "version": 4 22 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | 8 | ], 9 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 10 | "unwantedRecommendations": [ 11 | 12 | ] 13 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(lldb) Launch Run", 9 | "preLaunchTask": "Bazel Build (Run)", 10 | "type": "cppdbg", 11 | "request": "launch", 12 | "program": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}", 13 | "args": [], 14 | "stopAtEntry": false, 15 | "cwd": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}.runfiles/__main__", 16 | "environment": [], 17 | "externalConsole": false, 18 | "MIMode": "lldb" 19 | }, 20 | { 21 | "name": "(lldb) Launch Run Opt", 22 | "preLaunchTask": "Bazel Build (Run Opt)", 23 | "type": "cppdbg", 24 | "request": "launch", 25 | "program": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}", 26 | "args": [], 27 | "stopAtEntry": false, 28 | "cwd": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}.runfiles/__main__", 29 | "environment": [], 30 | "externalConsole": false, 31 | "MIMode": "lldb" 32 | }, 33 | { 34 | "name": "(lldb) Launch Debug", 35 | "preLaunchTask": "Bazel Build (Debug)", 36 | "type": "cppdbg", 37 | "request": "launch", 38 | "program": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}", 39 | "args": [], 40 | "stopAtEntry": false, 41 | "cwd": "${workspaceFolder}/bazel-bin/${relativeFileDirname}/${fileBasenameNoExtension}.runfiles/__main__", 42 | "environment": [], 43 | "externalConsole": false, 44 | "MIMode": "lldb" 45 | }, 46 | { 47 | "preLaunchTask": "Bazel Build (Debug)", 48 | "name": "CodeLLDB", 49 | "type": "lldb", 50 | "request": "launch", 51 | "program": "${workspaceFolder}/bazel-bin/src/main/${fileBasenameNoExtension}", 52 | "args": [], 53 | // *** You need to change this part for your own system to work *** 54 | // Set sourceMap from output of (readlink -n bazel-cpp-template) to ${workspaceFolder} 55 | "sourceMap": { 56 | "/private/var/tmp/_bazel_ari/8b6cf68db0ea2d6ea3f9c3ed5620d651/execroot/__main__": "${workspaceFolder}/" 57 | }, 58 | }, 59 | ] 60 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "string": "cpp", 4 | "filesystem": "cpp", 5 | "iosfwd": "cpp", 6 | "__bit_reference": "cpp", 7 | "__config": "cpp", 8 | "__debug": "cpp", 9 | "__errc": "cpp", 10 | "__functional_base": "cpp", 11 | "__hash_table": "cpp", 12 | "__locale": "cpp", 13 | "__mutex_base": "cpp", 14 | "__node_handle": "cpp", 15 | "__nullptr": "cpp", 16 | "__split_buffer": "cpp", 17 | "__string": "cpp", 18 | "__threading_support": "cpp", 19 | "__tree": "cpp", 20 | "__tuple": "cpp", 21 | "algorithm": "cpp", 22 | "array": "cpp", 23 | "atomic": "cpp", 24 | "bit": "cpp", 25 | "bitset": "cpp", 26 | "cctype": "cpp", 27 | "chrono": "cpp", 28 | "cinttypes": "cpp", 29 | "cmath": "cpp", 30 | "complex": "cpp", 31 | "cstdarg": "cpp", 32 | "cstddef": "cpp", 33 | "cstdint": "cpp", 34 | "cstdio": "cpp", 35 | "cstdlib": "cpp", 36 | "cstring": "cpp", 37 | "ctime": "cpp", 38 | "cwchar": "cpp", 39 | "cwctype": "cpp", 40 | "deque": "cpp", 41 | "exception": "cpp", 42 | "fstream": "cpp", 43 | "functional": "cpp", 44 | "initializer_list": "cpp", 45 | "iomanip": "cpp", 46 | "ios": "cpp", 47 | "iostream": "cpp", 48 | "istream": "cpp", 49 | "iterator": "cpp", 50 | "limits": "cpp", 51 | "locale": "cpp", 52 | "map": "cpp", 53 | "memory": "cpp", 54 | "mutex": "cpp", 55 | "new": "cpp", 56 | "optional": "cpp", 57 | "ostream": "cpp", 58 | "ratio": "cpp", 59 | "set": "cpp", 60 | "sstream": "cpp", 61 | "stack": "cpp", 62 | "stdexcept": "cpp", 63 | "streambuf": "cpp", 64 | "string_view": "cpp", 65 | "system_error": "cpp", 66 | "thread": "cpp", 67 | "tuple": "cpp", 68 | "type_traits": "cpp", 69 | "typeinfo": "cpp", 70 | "unordered_map": "cpp", 71 | "utility": "cpp", 72 | "vector": "cpp", 73 | "numeric": "cpp", 74 | "future": "cpp", 75 | "shared_mutex": "cpp", 76 | "condition_variable": "cpp", 77 | "random": "cpp", 78 | "codecvt": "cpp", 79 | "list": "cpp", 80 | "regex": "cpp", 81 | "forward_list": "cpp", 82 | "unordered_set": "cpp", 83 | "memory_resource": "cpp", 84 | "clocale": "cpp", 85 | "*.tcc": "cpp", 86 | "compare": "cpp", 87 | "concepts": "cpp", 88 | "numbers": "cpp", 89 | "semaphore": "cpp", 90 | "stop_token": "cpp", 91 | "cfenv": "cpp", 92 | "__verbose_abort": "cpp", 93 | "charconv": "cpp", 94 | "execution": "cpp", 95 | "print": "cpp", 96 | "queue": "cpp", 97 | "variant": "cpp", 98 | "any": "cpp", 99 | "span": "cpp", 100 | "jthread": "cpp" 101 | }, 102 | "lldb.displayFormat": "auto", 103 | "lldb.showDisassembly": "never", 104 | "lldb.dereferencePointers": true 105 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Modified to enable debugging using bazel 2 | { 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "label": "Bazel Build (Debug)", 7 | "type": "shell", 8 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} -c dbg", 9 | "windows": { 10 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} --experimental_enable_runfiles -c dbg" 11 | }, 12 | "osx": { 13 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} -c dbg --spawn_strategy=standalone", 14 | }, 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | }, 19 | }, 20 | { 21 | "label": "Bazel Build (Run)", 22 | "type": "shell", 23 | "command": "bazel build --cxxopt='-std=c++17' --run_under=lldb ${relativeFileDirname}:${fileBasenameNoExtension}", 24 | "windows": { 25 | "command": "bazel build --cxxopt='-std=c++17' --run_under=lldb ${relativeFileDirname}:${fileBasenameNoExtension}" 26 | }, 27 | "osx": { 28 | "command": "bazel build --cxxopt='-std=c++17' --run_under=lldb ${relativeFileDirname}:${fileBasenameNoExtension}", 29 | }, 30 | "group": { 31 | "kind": "build", 32 | "isDefault": true 33 | }, 34 | }, 35 | { 36 | "label": "Bazel Build (Run)", 37 | "type": "shell", 38 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension}", 39 | "windows": { 40 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension}" 41 | }, 42 | "osx": { 43 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension}", 44 | }, 45 | "group": { 46 | "kind": "build", 47 | "isDefault": true 48 | }, 49 | }, 50 | { 51 | "label": "Bazel Build (Run Opt)", 52 | "type": "shell", 53 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} -c opt", 54 | "windows": { 55 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} -c opt" 56 | }, 57 | "osx": { 58 | "command": "bazel build --cxxopt='-std=c++17' ${relativeFileDirname}:${fileBasenameNoExtension} -c opt", 59 | }, 60 | "group": { 61 | "kind": "build", 62 | "isDefault": true 63 | }, 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Bazel now uses Bzlmod by default to manage external dependencies. 3 | # Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel. 4 | # 5 | # For more details, please check https://github.com/bazelbuild/bazel/issues/18958 6 | ############################################################################### 7 | module(name = "multithreading_cpp", version = "0.1.0") 8 | 9 | bazel_dep(name = "rules_cc", version = "0.0.9") 10 | 11 | bazel_dep(name = "googletest", version = "1.14.0") 12 | bazel_dep(name = "google_benchmark", version = "1.9.2") 13 | -------------------------------------------------------------------------------- /MODULE.bazel.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockFileVersion": 18, 3 | "registryFileHashes": { 4 | "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", 5 | "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", 6 | "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", 7 | "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", 8 | "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", 9 | "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", 10 | "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", 11 | "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", 12 | "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", 13 | "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", 14 | "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", 15 | "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", 16 | "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", 17 | "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", 18 | "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", 19 | "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", 20 | "https://bcr.bazel.build/modules/bazel_features/1.21.0/source.json": "3e8379efaaef53ce35b7b8ba419df829315a880cb0a030e5bb45c96d6d5ecb5f", 21 | "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", 22 | "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", 23 | "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", 24 | "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", 25 | "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", 26 | "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", 27 | "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", 28 | "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", 29 | "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", 30 | "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", 31 | "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", 32 | "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", 33 | "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", 34 | "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", 35 | "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", 36 | "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", 37 | "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", 38 | "https://bcr.bazel.build/modules/google_benchmark/1.9.2/MODULE.bazel": "1d30f717f00d5f18e7d8e55d18573bab80651d75b40e3391af2992cd2568577a", 39 | "https://bcr.bazel.build/modules/google_benchmark/1.9.2/source.json": "fd301b911848c3ad83700d1bf5b90ad23afc24f25a9cc34d8297ab6203cfe39d", 40 | "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", 41 | "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", 42 | "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", 43 | "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", 44 | "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", 45 | "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", 46 | "https://bcr.bazel.build/modules/libpfm/4.11.0.bcr.1/MODULE.bazel": "e5362dadc90aab6724c83a2cc1e67cbed9c89a05d97fb1f90053c8deb1e445c8", 47 | "https://bcr.bazel.build/modules/libpfm/4.11.0.bcr.1/source.json": "0646414d9037f8aad148781dd760bec90b0b25ac12fda5e03f8aadbd6b9c61e6", 48 | "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", 49 | "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", 50 | "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", 51 | "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", 52 | "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", 53 | "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", 54 | "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", 55 | "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", 56 | "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", 57 | "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", 58 | "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", 59 | "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", 60 | "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", 61 | "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", 62 | "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", 63 | "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", 64 | "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", 65 | "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", 66 | "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", 67 | "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", 68 | "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", 69 | "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", 70 | "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", 71 | "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", 72 | "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", 73 | "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", 74 | "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", 75 | "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", 76 | "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", 77 | "https://bcr.bazel.build/modules/rules_cc/0.0.17/source.json": "4db99b3f55c90ab28d14552aa0632533e3e8e5e9aea0f5c24ac0014282c2a7c5", 78 | "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", 79 | "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", 80 | "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", 81 | "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", 82 | "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", 83 | "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", 84 | "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", 85 | "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", 86 | "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", 87 | "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", 88 | "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", 89 | "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", 90 | "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", 91 | "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", 92 | "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", 93 | "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", 94 | "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", 95 | "https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad", 96 | "https://bcr.bazel.build/modules/rules_java/8.11.0/source.json": "302b52a39259a85aa06ca3addb9787864ca3e03b432a5f964ea68244397e7544", 97 | "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", 98 | "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", 99 | "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", 100 | "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", 101 | "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", 102 | "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", 103 | "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", 104 | "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", 105 | "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", 106 | "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", 107 | "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", 108 | "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", 109 | "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", 110 | "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", 111 | "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", 112 | "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", 113 | "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", 114 | "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", 115 | "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", 116 | "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", 117 | "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", 118 | "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", 119 | "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", 120 | "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", 121 | "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", 122 | "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", 123 | "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", 124 | "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", 125 | "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", 126 | "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320", 127 | "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", 128 | "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95", 129 | "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", 130 | "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", 131 | "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", 132 | "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", 133 | "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", 134 | "https://bcr.bazel.build/modules/stardoc/0.7.1/source.json": "b6500ffcd7b48cd72c29bb67bcac781e12701cc0d6d55d266a652583cfcdab01", 135 | "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", 136 | "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", 137 | "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", 138 | "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d", 139 | "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" 140 | }, 141 | "selectedYankedVersions": {}, 142 | "moduleExtensions": { 143 | "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { 144 | "general": { 145 | "bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=", 146 | "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", 147 | "recordedFileInputs": {}, 148 | "recordedDirentsInputs": {}, 149 | "envVariables": {}, 150 | "generatedRepoSpecs": { 151 | "com_github_jetbrains_kotlin_git": { 152 | "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", 153 | "attributes": { 154 | "urls": [ 155 | "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" 156 | ], 157 | "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88" 158 | } 159 | }, 160 | "com_github_jetbrains_kotlin": { 161 | "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", 162 | "attributes": { 163 | "git_repository_name": "com_github_jetbrains_kotlin_git", 164 | "compiler_version": "1.9.23" 165 | } 166 | }, 167 | "com_github_google_ksp": { 168 | "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", 169 | "attributes": { 170 | "urls": [ 171 | "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" 172 | ], 173 | "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", 174 | "strip_version": "1.9.23-1.0.20" 175 | } 176 | }, 177 | "com_github_pinterest_ktlint": { 178 | "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", 179 | "attributes": { 180 | "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", 181 | "urls": [ 182 | "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint" 183 | ], 184 | "executable": true 185 | } 186 | }, 187 | "rules_android": { 188 | "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", 189 | "attributes": { 190 | "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", 191 | "strip_prefix": "rules_android-0.1.1", 192 | "urls": [ 193 | "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip" 194 | ] 195 | } 196 | } 197 | }, 198 | "recordedRepoMappingEntries": [ 199 | [ 200 | "rules_kotlin+", 201 | "bazel_tools", 202 | "bazel_tools" 203 | ] 204 | ] 205 | } 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi Threading Tutorial in C++ 2 | 3 | > Quick demonstration of multi threading in modern C++ 4 | 5 | Features: 6 | 7 | - [x] Creating task and threads 8 | - [x] Using function pointers, functors, and lambda functions 9 | - [x] Futures, promises, and async tasks 10 | - [x] Mutex and Locks 11 | - [x] Conditional Variables 12 | - [x] JThreads 13 | - [x] Supports Google [Bazel](https://bazel.build/) 14 | 15 | 16 | 17 | Here is the video explaining how to use multi-threading in C++: 18 | 19 |
20 | 21 | 22 | Debugging C++ in Visual Studio Code using gcc/gdb and Bazel 23 | 24 |
25 | 26 | ## Installation 27 | 28 | ```bash 29 | git clone https://github.com/ourarash/multithreading_cpp.git 30 | ``` 31 | 32 | ## Running examples: 33 | 34 | You can run this either using `bazel` OR just plain `g++`. 35 | 36 | 37 | For `bazel`: 38 | 39 | ```bash 40 | bazel run src/main:main 41 | ``` 42 | 43 | For `g++`: 44 | ```bash 45 | g++ -std=c++20 -lpthread src/main/main.cc -I ./ 46 | ./a.out 47 | ``` 48 | 49 | ## Bazel Installation and Visual Studio Setup 50 | For instructions on installing Bazel and setting it up in Visual Studio Code see [here](https://github.com/ourarash/cpp-template) -------------------------------------------------------------------------------- /a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourarash/multithreading_cpp/4069bad6a7a15693de5b6dbb9248b007e798c87c/a.out -------------------------------------------------------------------------------- /cpp_multithreading_timeline_wavy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourarash/multithreading_cpp/4069bad6a7a15693de5b6dbb9248b007e798c87c/cpp_multithreading_timeline_wavy.png -------------------------------------------------------------------------------- /lambda_jthread_main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourarash/multithreading_cpp/4069bad6a7a15693de5b6dbb9248b007e798c87c/lambda_jthread_main -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourarash/multithreading_cpp/4069bad6a7a15693de5b6dbb9248b007e798c87c/main -------------------------------------------------------------------------------- /src/lib/BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "utility_lib", 3 | # srcs = glob(["**/*.cc"]), 4 | hdrs = ["utility.h"], 5 | visibility = ["//visibility:public"], 6 | ) 7 | 8 | cc_library( 9 | name = "VectorAccumulatorLib", 10 | hdrs = ["vector_accumulator.h"], 11 | visibility = ["//visibility:public"], 12 | ) 13 | 14 | cc_library( 15 | name = "RangeAccumulatorLib", 16 | hdrs = ["range_accumulator.h"], 17 | visibility = ["//visibility:public"], 18 | deps = [ 19 | "@com_google_benchmark//:benchmark", 20 | ], 21 | ) 22 | 23 | cc_library( 24 | name = "logger", 25 | hdrs = ["logger.h"], 26 | visibility = ["//visibility:public"], 27 | ) 28 | -------------------------------------------------------------------------------- /src/lib/logger.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Logger { 4 | std::string name; 5 | Logger(const std::string &n) : name(n) { 6 | std::cout << "Constructing " << name << "\n"; 7 | } 8 | ~Logger() { std::cout << "Destructing " << name << "\n"; } 9 | }; -------------------------------------------------------------------------------- /src/lib/range_accumulator.h: -------------------------------------------------------------------------------- 1 | #ifndef B28D145E_0F50_427C_9F74_B7605DF90DAF 2 | #define B28D145E_0F50_427C_9F74_B7605DF90DAF 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "benchmark/benchmark.h" 11 | 12 | // A class for comparison of lockless and lockfull multithreading 13 | class RangeAccumulator { 14 | public: 15 | static void Init() { 16 | _mutex_sum = 0; 17 | _atomic_sum = 0; 18 | } 19 | 20 | // Accumulates values from low to high 21 | static void AtomicAccumulator(unsigned long low, unsigned long high) { 22 | for (unsigned long i = low; i < high; i++) { 23 | benchmark::DoNotOptimize(_atomic_sum += i); 24 | } 25 | } 26 | 27 | //----------------------------------------------------- 28 | // Accumulates values from low to high with relaxed memory order 29 | static void AtomicAccumulatorRelaxed(unsigned long low, unsigned long high) { 30 | for (unsigned long i = low; i < high; i++) { 31 | benchmark::DoNotOptimize( 32 | _atomic_sum.fetch_add(i, std::memory_order_relaxed)); 33 | } 34 | } 35 | //----------------------------------------------------- 36 | // Accumulates values from low to high using reduction and relaxed memory 37 | // order 38 | static void AtomicAccumulatorPartition(unsigned long low, 39 | unsigned long high) { 40 | unsigned long local_sum = 0; 41 | for (unsigned long i = low; i < high; i++) { 42 | benchmark::DoNotOptimize(local_sum += i); 43 | } 44 | _atomic_sum.fetch_add(local_sum, std::memory_order_seq_cst); 45 | } 46 | //----------------------------------------------------- 47 | // Accumulates values from low to high using reduction and relaxed memory 48 | // order 49 | static void AtomicAccumulatorPartitionRelaxed(unsigned long low, 50 | unsigned long high) { 51 | unsigned long local_sum = 0; 52 | for (unsigned long i = low; i < high; i++) { 53 | benchmark::DoNotOptimize(local_sum += i); 54 | } 55 | _atomic_sum.fetch_add(local_sum, std::memory_order_relaxed); 56 | } 57 | //----------------------------------------------------- 58 | // Accumulates values from low to high using reduction using mutex 59 | static void MutexAccumulatorPartition(unsigned long low, unsigned long high) { 60 | unsigned long local_sum = 0; 61 | for (unsigned long i = low; i < high; i++) { 62 | benchmark::DoNotOptimize(local_sum += i); 63 | } 64 | 65 | { 66 | std::lock_guard lg(_my_mutex); 67 | _mutex_sum += local_sum; 68 | } 69 | } 70 | //----------------------------------------------------- 71 | template 72 | static void Driver(unsigned long number_of_threads, T func, 73 | unsigned long size) { 74 | Init(); 75 | std::vector threads; 76 | 77 | unsigned long step = size / number_of_threads; 78 | unsigned long num_chunks = 79 | step == 0 ? 1 : std::ceil((double)size / (double)step); 80 | 81 | for (unsigned long i = 0; i < num_chunks; i++) { 82 | unsigned long low = i * step; 83 | unsigned long high = std::min((i + 1) * step, (unsigned long)size); 84 | high = std::max((unsigned long)0, high); 85 | threads.push_back(std::thread([&func, low, high]() { func(low, high); })); 86 | } 87 | 88 | for (std::thread &t : threads) { 89 | if (t.joinable()) { 90 | t.join(); 91 | } 92 | } 93 | } 94 | //----------------------------------------------------- 95 | public: 96 | static std::mutex _my_mutex; 97 | static unsigned long _mutex_sum; 98 | static std::atomic _atomic_sum; 99 | }; 100 | 101 | #endif /* B28D145E_0F50_427C_9F74_B7605DF90DAF */ 102 | -------------------------------------------------------------------------------- /src/lib/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_H 2 | #define UTILITY_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //----------------------------------------------------- 9 | template 10 | void PrintVector(std::vector input) { 11 | std::cout << "{ "; 12 | unsigned int count = 0; 13 | for (auto n : input) { 14 | count++; 15 | std::cout << n; 16 | if (count < input.size()) std::cout << ", "; 17 | } 18 | std::cout << " }" << std::endl; 19 | } 20 | //----------------------------------------------------- 21 | class AccumulateFunctor { 22 | public: 23 | void operator()(uint64_t start, uint64_t end) { 24 | _sum = 0; 25 | for (auto i = start; i < end; i++) { 26 | _sum += i; 27 | } 28 | std::cout << _sum << std::endl; 29 | } 30 | // ~AccumulateFunctor(){std::cout << "AccumulateFunctor Destructor." << std::endl;} 31 | uint64_t _sum; 32 | }; 33 | //----------------------------------------------------- 34 | void AccumulateRange(uint64_t &sum, uint64_t start, uint64_t end) { 35 | sum = 0; 36 | for (uint64_t i = start; i < end; i++) { 37 | sum += i; 38 | } 39 | } 40 | //----------------------------------------------------- 41 | uint64_t GetRangeSum(uint64_t start, uint64_t end) { 42 | uint64_t sum = 0; 43 | for (uint64_t i = start; i < end; i++) { 44 | sum += i; 45 | } 46 | return sum; 47 | } 48 | 49 | #endif -------------------------------------------------------------------------------- /src/lib/vector_accumulator.h: -------------------------------------------------------------------------------- 1 | #ifndef B70344C7_AE02_460A_926A_75DE4F08B159 2 | #define B70344C7_AE02_460A_926A_75DE4F08B159 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // A class for comparison of lockless and lockfull multithreading 11 | class VectorAccumulator { 12 | public: 13 | static void Init() { 14 | _mutex_sum = 0; 15 | _atomic_sum = 0; 16 | } 17 | static void AtomicAccumulator(std::vector &a, 18 | unsigned long low, unsigned long high) { 19 | for (unsigned long i = low; i < high; i++) { 20 | _atomic_sum += a[i]; 21 | } 22 | } 23 | 24 | //----------------------------------------------------- 25 | static void AtomicAccumulatorRelaxed(std::vector &a, 26 | unsigned long low, unsigned long high) { 27 | for (unsigned long i = low; i < high; i++) { 28 | _atomic_sum.fetch_add(a[i], std::memory_order_relaxed); 29 | } 30 | } 31 | //----------------------------------------------------- 32 | static void AtomicAccumulatorPartitionRelaxed(std::vector &a, 33 | unsigned long low, 34 | unsigned long high) { 35 | unsigned long local_sum = 0; 36 | for (unsigned long i = low; i < high; i++) { 37 | local_sum += a[i]; 38 | } 39 | _atomic_sum.fetch_add(local_sum, std::memory_order_relaxed); 40 | } 41 | //----------------------------------------------------- 42 | 43 | static void MutexAccumulatorPartition(std::vector &a, 44 | unsigned long low, unsigned long high) { 45 | unsigned long local_sum = 0; 46 | for (unsigned long i = low; i < high; i++) { 47 | local_sum += a[i]; 48 | } 49 | 50 | { 51 | std::lock_guard lg(_my_mutex); 52 | _mutex_sum += local_sum; 53 | } 54 | } 55 | //----------------------------------------------------- 56 | template 57 | static void Driver(std::vector &a, 58 | unsigned long number_of_threads, T func) { 59 | Init(); 60 | std::vector threads; 61 | 62 | unsigned long step = a.size() / number_of_threads; 63 | unsigned long num_chunks = 64 | step == 0 ? 1 : std::ceil((double)a.size() / (double)step); 65 | 66 | for (unsigned long i = 0; i < num_chunks; i++) { 67 | unsigned long low = i * step; 68 | unsigned long high = std::min((i + 1) * step, (unsigned long)a.size()); 69 | high = std::max((unsigned long)0, high); 70 | threads.push_back( 71 | std::thread([&a, &func, low, high]() { func(a, low, high); })); 72 | } 73 | 74 | for (std::thread &t : threads) { 75 | if (t.joinable()) { 76 | t.join(); 77 | } 78 | } 79 | } 80 | //----------------------------------------------------- 81 | public: 82 | static std::mutex _my_mutex; 83 | static unsigned long _mutex_sum; 84 | static std::atomic _atomic_sum; 85 | }; 86 | #endif /* B70344C7_AE02_460A_926A_75DE4F08B159 */ 87 | -------------------------------------------------------------------------------- /src/main/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_binary( 3 | name = "main", 4 | srcs = ["main.cc"], 5 | deps = ["//src/lib:utility_lib"], 6 | ) 7 | 8 | cc_binary( 9 | name = "jthread", 10 | srcs = ["jthread.cc"], 11 | copts = [ 12 | "-std=c++20", 13 | "-stdlib=libc++", 14 | ], 15 | deps = ["//src/lib:utility_lib"], 16 | ) 17 | 18 | cc_binary( 19 | name = "vector_of_threads_main", 20 | srcs = ["vector_of_threads_main.cc"], 21 | deps = ["//src/lib:utility_lib"], 22 | ) 23 | 24 | cc_binary( 25 | name = "lambda_main", 26 | srcs = ["lambda_main.cc"], 27 | deps = ["//src/lib:utility_lib"], 28 | ) 29 | 30 | cc_binary( 31 | name = "lambda_jthread_main", 32 | srcs = ["lambda_jthread_main.cc"], 33 | copts = [ 34 | "-std=c++20", 35 | "-stdlib=libc++", 36 | ], 37 | deps = ["//src/lib:utility_lib"], 38 | ) 39 | 40 | cc_binary( 41 | name = "functors_main", 42 | srcs = ["functors_main.cc"], 43 | deps = ["//src/lib:utility_lib"], 44 | ) 45 | 46 | cc_binary( 47 | name = "functors_main_unique_ptr", 48 | srcs = ["functors_main_unique_ptr.cc"], 49 | deps = ["//src/lib:utility_lib"], 50 | ) 51 | 52 | cc_binary( 53 | name = "async_main", 54 | srcs = ["async_main.cc"], 55 | deps = ["//src/lib:utility_lib"], 56 | ) 57 | 58 | cc_binary( 59 | name = "async_lambda_main", 60 | srcs = ["async_lambda_main.cc"], 61 | deps = ["//src/lib:utility_lib"], 62 | ) 63 | 64 | cc_binary( 65 | name = "number_of_cores", 66 | srcs = ["number_of_cores.cc"], 67 | deps = ["//src/lib:utility_lib"], 68 | ) 69 | -------------------------------------------------------------------------------- /src/main/async_lambda_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for creating two threads 2 | // Run this using one of the following methods: 3 | // 1. With bazel: bazel run src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 4 | // 2. With plain g++: g++ -std=c++17 -lpthread 5 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "src/lib/utility.h" 13 | 14 | int main() { 15 | const int number_of_threads = 10; 16 | uint64_t number_of_elements = 1000 * 1000 * 1000; 17 | uint64_t step = number_of_elements / number_of_threads; 18 | std::vector> tasks; 19 | std::vector partial_sums(number_of_threads); 20 | 21 | for (uint64_t i = 0; i < number_of_threads; i++) { 22 | tasks.push_back(std::async([i, step] { 23 | uint64_t r = 0; 24 | for (uint64_t j = i * step; j < (i + 1) * step; j++) { 25 | r += j; 26 | } 27 | return r; 28 | })); 29 | } 30 | 31 | uint64_t total = 0; 32 | for (auto &t : tasks) { 33 | total += t.get(); 34 | } 35 | 36 | std::cout << "total: " << total << std::endl; 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /src/main/async_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "src/lib/utility.h" 8 | 9 | // A demo for creating two threads 10 | // Run this using one of the following methods: 11 | // 1. With bazel: bazel run src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 12 | // 2. With plain g++: g++ -std=c++17 -lpthread 13 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 14 | int main() { 15 | const int number_of_threads = 20; 16 | uint64_t number_of_elements = 1000 * 1000 * 1000; 17 | uint64_t step = number_of_elements / number_of_threads; 18 | std::vector> tasks; 19 | 20 | for (uint64_t i = 0; i < number_of_threads; i++) { 21 | tasks.push_back(std::async(std::launch 22 | : async, GetRangeSum, i * step, (i + 1) * step)); 23 | } 24 | 25 | uint64_t total = 0; 26 | for (auto &t : tasks) { 27 | auto p = t.get(); 28 | std::cout << "p: " << p << std::endl; 29 | total += p; 30 | } 31 | 32 | std::cout << "total: " << total << std::endl; 33 | 34 | return 0; 35 | } -------------------------------------------------------------------------------- /src/main/atomic/BUILD: -------------------------------------------------------------------------------- 1 | cc_binary( 2 | name = "vector_accumulator_benchmark", 3 | srcs = ["vector_accumulator_benchmark.cc"], 4 | deps = [ 5 | "//src/lib:VectorAccumulatorLib", 6 | "@com_google_benchmark//:benchmark", 7 | ], 8 | ) 9 | 10 | cc_binary( 11 | name = "range_accumulator_benchmark", 12 | srcs = ["range_accumulator_benchmark.cc"], 13 | deps = [ 14 | "//src/lib:RangeAccumulatorLib", 15 | "@com_google_benchmark//:benchmark", 16 | ], 17 | ) 18 | 19 | cc_binary( 20 | name = "atomic_demo_main", 21 | srcs = ["atomic_demo_main.cc"], 22 | deps = [ 23 | "@com_google_benchmark//:benchmark", 24 | ], 25 | ) 26 | 27 | cc_binary( 28 | name = "compare_exchange_main", 29 | srcs = ["compare_exchange_main.cc"], 30 | deps = [ 31 | "@com_google_benchmark//:benchmark", 32 | ], 33 | ) 34 | 35 | cc_binary( 36 | name = "lock_free_main", 37 | srcs = ["lock_free_main.cc"], 38 | deps = [ 39 | "@com_google_benchmark//:benchmark", 40 | ], 41 | ) 42 | 43 | cc_binary( 44 | name = "fetch_main", 45 | srcs = ["fetch_main.cc"], 46 | deps = [ 47 | "@com_google_benchmark//:benchmark", 48 | ], 49 | ) 50 | 51 | cc_binary( 52 | name = "sequential_consistency_main", 53 | srcs = ["sequential_consistency_main.cc"], 54 | deps = [ 55 | "@com_google_benchmark//:benchmark", 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /src/main/atomic/atomic_demo_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class Counters { 8 | int a; 9 | int b; 10 | }; // user-defined trivially-copyable type 11 | std::atomic cnt; 12 | 13 | class A { 14 | int myInt1; 15 | int myInt2; 16 | long myLong1; 17 | long myLong2; 18 | int* myPtr; 19 | 20 | A(int val = 1) { myInt1 = myInt2 = myLong1 = myLong2 = 1; } 21 | A(int i1, int i2, long l1, long l2) { 22 | myInt1 = i1; 23 | myInt2 = i2; 24 | myLong1 = l1; 25 | myLong2 = l2; 26 | } 27 | 28 | // A(const A& rhs) 29 | // : myInt1(rhs.myInt1), 30 | // myInt2(rhs.myInt2), 31 | // myLong1(rhs.myLong1), 32 | // myLong2(rhs.myLong2) {} // user-defined copy ctor 33 | 34 | void IncrementMyInt1() { myInt1++; } 35 | // A(const A&& a) { } // user-defined copy ctor 36 | // A& operator = (const A& rhs) { return *this;} // user-defined copy ctor 37 | A operator++() { 38 | myInt1++; 39 | myInt2++; 40 | myLong1++; 41 | myLong2++; 42 | return A(myInt1, myInt2, myLong1, myLong2); 43 | } // user-defined increment 44 | }; 45 | // std::atomic> v; 46 | 47 | int main() { 48 | // Demos is_trivially_copyable 49 | { 50 | std::atomic a; 51 | 52 | auto is_trivially_copyable = std::is_trivially_copyable::value; 53 | auto v_isTrivial = std::is_trivially_copyable>::value; 54 | std::cout << "is_trivially_copyable: " << is_trivially_copyable 55 | << std::endl; 56 | std::cout << "v_isTrivial: " << v_isTrivial << std::endl; 57 | } 58 | // Assignment non-atomic to atomic 59 | { 60 | std::atomic a(1); 61 | int b = 2; 62 | a = b; 63 | 64 | std::cout << "a: " << a << ", b: " << b << std::endl; 65 | } 66 | // Assignment non-atomic to atomic 67 | { 68 | struct A { 69 | int a[100]; 70 | }; 71 | std::atomic a1, a2; 72 | A a3; 73 | a1 = a3; 74 | } 75 | // Assignment atomic to atomic 76 | { 77 | std::atomic a(1), b(2); 78 | std::cout << "a: " << a << ", b: " << b << std::endl; 79 | // a = b; // Doesn't compile! 80 | a.store(b.load()); 81 | std::cout << "a: " << a << ", b: " << b << std::endl; 82 | } 83 | // Integer 84 | { 85 | std::atomic x_int(0); 86 | std::atomic y_int{0}; 87 | std::atomic z_int = {0}; 88 | // std::atomic t_int = 0; // Doesn't compile! 89 | std::atomic t_int; 90 | t_int = 0; // Compiles! 91 | 92 | x_int++; 93 | z_int++; 94 | 95 | std::cout << "x_int: " << x_int << std::endl; 96 | std::cout << "z_int: " << z_int << std::endl; 97 | } 98 | // load/store 99 | { 100 | std::atomic atomic_x(1); 101 | int y(2); 102 | 103 | y = atomic_x; 104 | atomic_x = y; 105 | 106 | // OR 107 | y = atomic_x.load(); 108 | atomic_x.store(y); 109 | } 110 | // load/store with tags 111 | { 112 | std::atomic atomic_x(1); 113 | int y(2); 114 | 115 | y = atomic_x; 116 | atomic_x = y; 117 | 118 | // OR 119 | y = atomic_x.load(std::memory_order_relaxed); 120 | atomic_x.store(y, std::memory_order_relaxed); 121 | } 122 | // exchange 123 | std::cout << "Exchange with non atomic: " << std::endl; 124 | 125 | { 126 | std::atomic atomic_x(1); 127 | int y(2); 128 | auto z = atomic_x.exchange(y); 129 | 130 | std::cout << "atomic_x: " << atomic_x << ", y: " << y << ", z: " << z 131 | << std::endl; 132 | } 133 | // exchange 134 | std::cout << "Exchange with atomic: " << std::endl; 135 | { 136 | std::atomic atomic_x(1); 137 | std::atomic atomic_y(2); 138 | auto z = atomic_x.exchange(atomic_y); 139 | 140 | std::cout << "atomic_x: " << atomic_x << ", atomic_y: " << atomic_y 141 | << ", z: " << z << std::endl; 142 | } 143 | // exchange 144 | { 145 | // if (atomic_x == expected) { 146 | // atomic_x = desired; 147 | // return true; 148 | // } else { 149 | // atomic_x = expected; 150 | // return false; 151 | // } 152 | std::atomic atomic_x(1); 153 | int expected = 2; 154 | int desired = 3; 155 | 156 | bool success = atomic_x.compare_exchange_strong(expected, desired); 157 | 158 | std::cout << "success: " << success << ", atomic_x: " << atomic_x 159 | << ", expected: " << expected << ", desired: " << desired 160 | << std::endl; 161 | 162 | success = atomic_x.compare_exchange_strong(expected, desired); 163 | 164 | std::cout << "success: " << success << ", atomic_x: " << atomic_x 165 | << ", expected: " << expected << ", desired: " << desired 166 | << std::endl; 167 | } 168 | // Double 169 | { 170 | std::atomic x_double(0); 171 | 172 | // x_double++; // Doesn't compile! 173 | std::cout << "x_double: " << x_double << std::endl; 174 | } 175 | 176 | //----------------------------------------------------- 177 | // Exchange: 178 | { 179 | std::atomic x_int(0); 180 | 181 | int y_int = 2; 182 | std::cout << "Before: x_int: " << x_int << ", y_int: " << y_int 183 | << std::endl; 184 | // Exchange is equivalent to: z=x, x=y 185 | int z_int = x_int.exchange(y_int); 186 | std::cout << "After: x_int: " << x_int << ", y_int: " << y_int 187 | << ", z_int: " << z_int << std::endl; 188 | } 189 | // Exchange with z,y atomic: 190 | { 191 | std::atomic x_int(0); 192 | std::atomic z_int(3); 193 | std::atomic y_int(2); 194 | std::cout << "Before: x_int: " << x_int << ", y_int: " << y_int 195 | << std::endl; 196 | // Exchange is equivalent to: z=x, x=y 197 | z_int = x_int.exchange(y_int); 198 | std::cout << "After: x_int: " << x_int << ", y_int: " << y_int 199 | << ", z_int: " << z_int << std::endl; 200 | } 201 | // Exchange with z atomic: 202 | { 203 | std::atomic x_int(0); 204 | std::atomic z_int(0); 205 | 206 | int y_int = 2; 207 | std::cout << "Before: x_int: " << x_int << ", y_int: " << y_int 208 | << std::endl; 209 | // Exchange is equivalent to: z=x, x=y 210 | z_int = x_int.exchange(y_int); 211 | std::cout << "After: x_int: " << x_int << ", y_int: " << y_int 212 | << ", z_int: " << z_int << std::endl; 213 | } 214 | // Compare Exchange: 215 | std::cout << "Exchange unsuccessful: " << std::endl; 216 | { 217 | std::atomic x_int(0); 218 | 219 | int y_int = 2; 220 | std::cout << "Before: x_int: " << x_int << ", y_int: " << y_int 221 | << std::endl; 222 | // compare_exchange_strong is equivalent to: if (x==y) then {x=z; return 223 | // true;} 224 | // else {y=x; return false;} 225 | int z_int = 5; 226 | bool success = x_int.compare_exchange_strong(y_int, z_int); 227 | std::cout << "After: x_int: " << x_int << ", y_int: " << y_int 228 | << ", z_int: " << z_int << std::endl; 229 | } 230 | //----------------------------------------------------- 231 | // Exchange: 232 | std::cout << "Exchange successful: " << std::endl; 233 | { 234 | std::atomic x_int(2); 235 | 236 | int y_int = 2; 237 | std::cout << "Before: x_int: " << x_int << ", y_int: " << y_int 238 | << std::endl; 239 | // compare_exchange_strong is equivalent to: if (x==y) then {x=z; return 240 | // true;} 241 | // else {y=x; return false;} 242 | int z_int = 5; 243 | bool success = x_int.compare_exchange_strong(y_int, z_int); 244 | std::cout << "After: x_int: " << x_int << ", y_int: " << y_int 245 | << ", z_int: " << z_int << std::endl; 246 | 247 | } 248 | //----------------------------------------------------- 249 | 250 | return 0; 251 | } 252 | -------------------------------------------------------------------------------- /src/main/atomic/compare_exchange_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::atomic g_atomicX{0}; 8 | int f(int a, int b) { return a * a + b; } 9 | 10 | int main() { 11 | // compare exchange 12 | { 13 | // if (atomic_x == expected) { 14 | // atomic_x = desired; 15 | // return true; 16 | // } else { 17 | // atomic_x = expected; 18 | // return false; 19 | // } 20 | std::atomic atomic_x(1); 21 | int expected = 2; 22 | int desired = 3; 23 | 24 | bool success = atomic_x.compare_exchange_strong(expected, desired); 25 | 26 | std::cout << "success: " << success << ", atomic_x: " << atomic_x 27 | << ", expected: " << expected << ", desired: " << desired 28 | << std::endl; 29 | 30 | success = atomic_x.compare_exchange_strong(expected, desired); 31 | 32 | std::cout << "success: " << success << ", atomic_x: " << atomic_x 33 | << ", expected: " << expected << ", desired: " << desired 34 | << std::endl; 35 | } 36 | 37 | { 38 | // Suppose we want to do the following atomically: 39 | g_atomicX = f(g_atomicX, 10); 40 | // Or 41 | g_atomicX = f(g_atomicX.load(), 10); 42 | // Or 43 | g_atomicX.store(f(g_atomicX.load(), 10)); 44 | 45 | // None of the above alternatives is atomic 46 | // Using compare_exchange: 47 | g_atomicX = 4; 48 | auto oldX = g_atomicX.load(); 49 | while (!g_atomicX.compare_exchange_strong(oldX, f(oldX, 10))) 50 | ; 51 | 52 | std::cout << "g_atomicX: " << g_atomicX << ", oldX: " << oldX << std::endl; 53 | } 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /src/main/atomic/fedoro_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::atomic sum; 5 | 6 | void do_work_atomic(size_t N, unsigned long* a) { 7 | for (size_t i = ; i < N; i++) { 8 | sum += a[i]; 9 | } 10 | } 11 | 12 | unsigned long sum(0); 13 | std::mutex M; 14 | void do_work_mutex(size_t N, unsigned long* a) { 15 | unsigned long s = 0; 16 | for (size_t i = ; i < N; i++) { 17 | s += a[i]; 18 | } 19 | std::lock_guard L(M); 20 | sum += s; 21 | } -------------------------------------------------------------------------------- /src/main/atomic/fetch_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::atomic data_1, data_2; 8 | 9 | void do_work_1() { data_1++; } 10 | void do_work_2() { data_2.fetch_add(1); } 11 | 12 | int main() { 13 | std::vector threads; 14 | 15 | for (int i = 0; i < 5; i++) { 16 | threads.push_back(std::thread(do_work_1)); 17 | threads.push_back(std::thread(do_work_2)); 18 | } 19 | 20 | for (std::thread &t : threads) { 21 | if (t.joinable()) { 22 | t.join(); 23 | } 24 | } 25 | 26 | std::cout << "data_1: " << data_1 << std::endl; 27 | std::cout << "data_2: " << data_2 << std::endl; 28 | auto y = data_2 += 7; 29 | std::cout << "y: " << y << std::endl; 30 | assert(data_1 == data_2); 31 | 32 | 33 | } -------------------------------------------------------------------------------- /src/main/atomic/lock_free_main.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | struct A { 9 | int a[100]; 10 | // long a; 11 | // int b; 12 | }; 13 | struct B { 14 | int x, y; 15 | }; 16 | 17 | struct C { 18 | int a[100]; 19 | }; 20 | std::atomic a; 21 | std::atomic b; 22 | std::atomic c; 23 | std::atomic d; 24 | std::cout << std::boolalpha << "std::atomic is lock free? " 25 | << a.is_lock_free() << '\n' 26 | << "std::atomic is lock free? " << b.is_lock_free() << '\n'; 27 | 28 | std::cout << std::boolalpha << "std::atomic is lock free? " 29 | << std::atomic_is_lock_free(&a) << '\n' 30 | << "std::atomic is lock free? " << std::atomic_is_lock_free(&b) 31 | << '\n'; 32 | 33 | std::cout << "c.is_always_lock_free: " << c.is_always_lock_free << std::endl; 34 | 35 | std::cout << std::boolalpha << "d.is_lock_free (): " << d.is_lock_free() 36 | << std::endl; 37 | 38 | // static constexpr bool is_always_lock_free = /*implementation-defined*/; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/main/atomic/plot.py: -------------------------------------------------------------------------------- 1 | # Python script to plot outputs of Google Benchmark in CSV format 2 | # By Ari Saif 3 | # ------------------------------------------------------------------------------ 4 | # usage: plot.py [-h] [--xLabel [XLABEL]] [--title [TITLE]] 5 | # [--series SERIES [SERIES ...]] 6 | # INPUT [INPUT ...] 7 | 8 | # Plots benchmark CSV outputs. 9 | 10 | # positional arguments: 11 | # INPUT input file name 12 | 13 | # optional arguments: 14 | # -h, --help show this help message and exit 15 | # --xLabel [XLABEL] Label for the X axis 16 | # --title [TITLE] Title of the plot 17 | # --series SERIES [SERIES ...] 18 | # Series names to be shown on the plot 19 | # ------------------------------------------------------------------------------ 20 | # Example call: 21 | # python3 plot.py src/benchmark/outputs/step4_random_with_check.csv --series "real_time_BM_IntroSortPar" "real_time_BM_StdSort" 22 | # ------------------------------------------------------------------------------ 23 | import argparse 24 | 25 | import matplotlib.pyplot as plt 26 | import pandas as pd 27 | import numpy as np 28 | from functools import reduce 29 | 30 | # Set input arguments 31 | parser = argparse.ArgumentParser(description='Plots benchmark CSV outputs.') 32 | parser.add_argument('input', metavar='INPUT', type=str, nargs='+', 33 | help='input file name') 34 | 35 | parser.add_argument('--xLabel', dest='xLabel', 36 | default="n", type=str, nargs='?', 37 | help='Label for the X axis') 38 | 39 | parser.add_argument('--title', dest='title', 40 | default='Run Time', type=str, nargs='?', 41 | help='Title of the plot') 42 | 43 | parser.add_argument('--series', dest='series', 44 | type=str, nargs='+', 45 | help='Series names to be shown on the plot') 46 | 47 | args = parser.parse_args() 48 | 49 | # Read input CSV file 50 | df = pd.read_csv(args.input[0]) 51 | 52 | #Remove '/' from the names and create a new column representing the size (n) 53 | df[['name', 'n']] = df.name.str.split("/", expand=True) 54 | df = df.filter(['name', 'n', 'real_time', 'cpu_time']) 55 | 56 | # Filter out BigO and RMS rows 57 | df = df[~df['name'].str.contains("BigO")] 58 | df = df[~df['name'].str.contains("RMS")] 59 | 60 | #Only keep n, name, and real_time columns 61 | df = df.filter(['n', 'name', 'real_time']) 62 | print(df) 63 | 64 | # Unstack data from various benchmarks and put them in new columns 65 | dfs = [ 66 | g.drop('name', 1).add_suffix(f'_{k}').rename({f'n_{k}': 'n'}, axis=1) 67 | for k, g in df.groupby('name') 68 | ] 69 | 70 | df1 = reduce(lambda x, y: pd.merge(x, y, on='n'), dfs) 71 | 72 | 73 | # Filter out the series that we want to be in the plot using --series 74 | if args.series and len(args.series) > 0: 75 | columns_to_show = ['n'] + args.series 76 | df1 = df1.filter(columns_to_show) 77 | 78 | print(df1) 79 | 80 | #Remove real_time_BM_ prefix from column names 81 | df1.columns = [x.strip().replace('real_time_BM_', '') for x in df1.columns] 82 | 83 | #Draw the plot 84 | ax = df1.plot(x='n', title=args.title, 85 | figsize=(15, 10), legend=True, fontsize=12, rot=0) 86 | 87 | ax.set_xticks(np.arange(len(df1['n']))) 88 | ax.set_xticklabels(df1['n'], rotation=45) 89 | ax.legend(fontsize=16) 90 | 91 | plt.show() 92 | -------------------------------------------------------------------------------- /src/main/atomic/range_accumulator_benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "benchmark/benchmark.h" 9 | #include "src/lib/range_accumulator.h" 10 | 11 | const unsigned long g_size = 10000000; 12 | std::mutex RangeAccumulator::_my_mutex; 13 | unsigned long RangeAccumulator::_mutex_sum(0); 14 | std::atomic RangeAccumulator::_atomic_sum(0); 15 | 16 | //----------------------------------------------------- 17 | static void BM_AtomicAccumulator(benchmark::State& state) { 18 | for (auto _ : state) { 19 | state.PauseTiming(); 20 | unsigned long number_of_threads = state.range(0); 21 | 22 | state.ResumeTiming(); 23 | 24 | RangeAccumulator::Driver(number_of_threads, 25 | RangeAccumulator::AtomicAccumulator, g_size); 26 | } 27 | } 28 | //----------------------------------------------------- 29 | 30 | static void BM_AtomicAccumulatorRelaxed(benchmark::State& state) { 31 | for (auto _ : state) { 32 | state.PauseTiming(); 33 | unsigned long number_of_threads = state.range(0); 34 | 35 | state.ResumeTiming(); 36 | 37 | RangeAccumulator::Driver( 38 | number_of_threads, RangeAccumulator::AtomicAccumulatorRelaxed, g_size); 39 | } 40 | } 41 | //----------------------------------------------------- 42 | static void BM_AtomicAccumulatorPartition(benchmark::State& state) { 43 | for (auto _ : state) { 44 | state.PauseTiming(); 45 | unsigned long number_of_threads = state.range(0); 46 | 47 | state.ResumeTiming(); 48 | 49 | RangeAccumulator::Driver(number_of_threads, 50 | RangeAccumulator::AtomicAccumulatorPartition, 51 | g_size); 52 | } 53 | } 54 | //----------------------------------------------------- 55 | static void BM_AtomicAccumulatorPartitionRelaxed(benchmark::State& state) { 56 | for (auto _ : state) { 57 | state.PauseTiming(); 58 | unsigned long number_of_threads = state.range(0); 59 | 60 | state.ResumeTiming(); 61 | 62 | RangeAccumulator::Driver( 63 | number_of_threads, RangeAccumulator::AtomicAccumulatorPartitionRelaxed, 64 | g_size); 65 | } 66 | } 67 | //----------------------------------------------------- 68 | static void BM_MutexAccumulatorPartition(benchmark::State& state) { 69 | for (auto _ : state) { 70 | state.PauseTiming(); 71 | unsigned long number_of_threads = state.range(0); 72 | 73 | state.ResumeTiming(); 74 | 75 | RangeAccumulator::Driver( 76 | number_of_threads, RangeAccumulator::MutexAccumulatorPartition, g_size); 77 | } 78 | } 79 | //----------------------------------------------------- 80 | 81 | BENCHMARK(BM_AtomicAccumulator)->DenseRange(1, 12, 1); 82 | BENCHMARK(BM_AtomicAccumulatorRelaxed)->DenseRange(1, 12, 1); 83 | 84 | 85 | BENCHMARK(BM_AtomicAccumulatorPartition)->DenseRange(1, 12, 1); 86 | BENCHMARK(BM_AtomicAccumulatorPartitionRelaxed)->DenseRange(1, 12, 1); 87 | 88 | BENCHMARK(BM_MutexAccumulatorPartition)->DenseRange(1, 12, 1); 89 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /src/main/atomic/range_accumulator_benchmark.csv: -------------------------------------------------------------------------------- 1 | name,iterations,real_time,cpu_time,time_unit,bytes_per_second,items_per_second,label,error_occurred,error_message 2 | "BM_AtomicAccumulator/1",100,5.26564e+07,33140,ns,,,,, 3 | "BM_AtomicAccumulator/2",100,1.54575e+08,44180,ns,,,,, 4 | "BM_AtomicAccumulator/3",100,1.47406e+08,65510,ns,,,,, 5 | "BM_AtomicAccumulator/4",100,1.53544e+08,64640,ns,,,,, 6 | "BM_AtomicAccumulator/5",100,1.62936e+08,78360,ns,,,,, 7 | "BM_AtomicAccumulator/6",100,1.77088e+08,117740,ns,,,,, 8 | "BM_AtomicAccumulator/7",100,1.82039e+08,131770,ns,,,,, 9 | "BM_AtomicAccumulator/8",100,1.88425e+08,126920,ns,,,,, 10 | "BM_AtomicAccumulator/9",100,1.86875e+08,171500,ns,,,,, 11 | "BM_AtomicAccumulator/10",100,1.83504e+08,174130,ns,,,,, 12 | "BM_AtomicAccumulator/11",100,1.80784e+08,235220,ns,,,,, 13 | "BM_AtomicAccumulator/12",100,1.868e+08,310930,ns,,,,, 14 | "BM_AtomicAccumulatorRelaxed/1",100,4.56665e+07,24460,ns,,,,, 15 | "BM_AtomicAccumulatorRelaxed/2",100,1.3494e+08,38560,ns,,,,, 16 | "BM_AtomicAccumulatorRelaxed/3",100,1.50199e+08,61010,ns,,,,, 17 | "BM_AtomicAccumulatorRelaxed/4",100,1.52887e+08,62400,ns,,,,, 18 | "BM_AtomicAccumulatorRelaxed/5",100,1.61322e+08,77590,ns,,,,, 19 | "BM_AtomicAccumulatorRelaxed/6",100,1.88128e+08,107780,ns,,,,, 20 | "BM_AtomicAccumulatorRelaxed/7",100,1.85976e+08,118560,ns,,,,, 21 | "BM_AtomicAccumulatorRelaxed/8",100,1.86711e+08,124430,ns,,,,, 22 | "BM_AtomicAccumulatorRelaxed/9",100,1.87443e+08,177130,ns,,,,, 23 | "BM_AtomicAccumulatorRelaxed/10",100,1.88156e+08,177190,ns,,,,, 24 | "BM_AtomicAccumulatorRelaxed/11",100,1.85328e+08,239530,ns,,,,, 25 | "BM_AtomicAccumulatorRelaxed/12",100,1.84374e+08,272750,ns,,,,, 26 | "BM_AtomicAccumulatorPartition/1",1000,1.36882e+07,18517,ns,,,,, 27 | "BM_AtomicAccumulatorPartition/2",1000,6.9806e+06,34370,ns,,,,, 28 | "BM_AtomicAccumulatorPartition/3",1000,4.77417e+06,56788,ns,,,,, 29 | "BM_AtomicAccumulatorPartition/4",1000,3.89682e+06,63111,ns,,,,, 30 | "BM_AtomicAccumulatorPartition/5",1000,3.54586e+06,86304,ns,,,,, 31 | "BM_AtomicAccumulatorPartition/6",1000,3.1625e+06,117834,ns,,,,, 32 | "BM_AtomicAccumulatorPartition/7",1000,3.09805e+06,126895,ns,,,,, 33 | "BM_AtomicAccumulatorPartition/8",1000,2.77798e+06,133919,ns,,,,, 34 | "BM_AtomicAccumulatorPartition/9",1000,2.54517e+06,172288,ns,,,,, 35 | "BM_AtomicAccumulatorPartition/10",3854,2.40882e+06,181574,ns,,,,, 36 | "BM_AtomicAccumulatorPartition/11",3014,2.54102e+06,239370,ns,,,,, 37 | "BM_AtomicAccumulatorPartition/12",2409,2.32916e+06,292127,ns,,,,, 38 | "BM_AtomicAccumulatorPartitionRelaxed/1",1000,1.36622e+07,18106,ns,,,,, 39 | "BM_AtomicAccumulatorPartitionRelaxed/2",1000,6.98588e+06,34219,ns,,,,, 40 | "BM_AtomicAccumulatorPartitionRelaxed/3",1000,4.71654e+06,58306,ns,,,,, 41 | "BM_AtomicAccumulatorPartitionRelaxed/4",1000,3.77509e+06,61072,ns,,,,, 42 | "BM_AtomicAccumulatorPartitionRelaxed/5",1000,3.27287e+06,79511,ns,,,,, 43 | "BM_AtomicAccumulatorPartitionRelaxed/6",1000,3.06713e+06,114240,ns,,,,, 44 | "BM_AtomicAccumulatorPartitionRelaxed/7",1000,3.08407e+06,126803,ns,,,,, 45 | "BM_AtomicAccumulatorPartitionRelaxed/8",1000,2.78305e+06,132428,ns,,,,, 46 | "BM_AtomicAccumulatorPartitionRelaxed/9",1000,2.52854e+06,170469,ns,,,,, 47 | "BM_AtomicAccumulatorPartitionRelaxed/10",3814,2.41593e+06,182611,ns,,,,, 48 | "BM_AtomicAccumulatorPartitionRelaxed/11",2990,2.29236e+06,235238,ns,,,,, 49 | "BM_AtomicAccumulatorPartitionRelaxed/12",2377,2.25236e+06,286544,ns,,,,, 50 | "BM_MutexAccumulatorPartition/1",1000,1.33933e+07,18484,ns,,,,, 51 | "BM_MutexAccumulatorPartition/2",1000,6.8271e+06,33528,ns,,,,, 52 | "BM_MutexAccumulatorPartition/3",1000,4.63846e+06,57889,ns,,,,, 53 | "BM_MutexAccumulatorPartition/4",1000,3.6426e+06,61201,ns,,,,, 54 | "BM_MutexAccumulatorPartition/5",1000,3.32292e+06,80160,ns,,,,, 55 | "BM_MutexAccumulatorPartition/6",1000,3.03389e+06,112271,ns,,,,, 56 | "BM_MutexAccumulatorPartition/7",1000,2.95161e+06,123608,ns,,,,, 57 | "BM_MutexAccumulatorPartition/8",1000,2.64782e+06,128517,ns,,,,, 58 | "BM_MutexAccumulatorPartition/9",1000,2.64238e+06,183985,ns,,,,, 59 | "BM_MutexAccumulatorPartition/10",1000,3.2008e+06,239562,ns,,,,, 60 | "BM_MutexAccumulatorPartition/11",3131,2.20684e+06,225593,ns,,,,, 61 | "BM_MutexAccumulatorPartition/12",2473,2.16403e+06,284902,ns,,,,, 62 | -------------------------------------------------------------------------------- /src/main/atomic/vector_accumulator_benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "benchmark/benchmark.h" 9 | #include "src/lib/vector_accumulator.h" 10 | 11 | class Data { 12 | public: 13 | Data(unsigned long size) { 14 | v.resize(size); 15 | 16 | std::random_device rnd_device; 17 | // Specify the engine and distribution. 18 | std::mt19937 mersenne_engine{rnd_device()}; // Generates random integers 19 | std::uniform_int_distribution dist{1, 5}; 20 | 21 | auto gen = [&dist, &mersenne_engine]() { return dist(mersenne_engine); }; 22 | 23 | generate(std::begin(v), std::end(v), gen); 24 | } 25 | std::vector v; 26 | }; 27 | 28 | const unsigned long g_size = 1000000000; 29 | const int g_max_num_threads = 15; 30 | std::mutex VectorAccumulator::_my_mutex; 31 | unsigned long VectorAccumulator::_mutex_sum(0); 32 | std::atomic VectorAccumulator::_atomic_sum(0); 33 | 34 | //----------------------------------------------------- 35 | 36 | static void BM_AtomicAccumulator(benchmark::State& state) { 37 | for (auto _ : state) { 38 | state.PauseTiming(); 39 | unsigned long number_of_threads = state.range(0); 40 | Data d(g_size); 41 | 42 | state.ResumeTiming(); 43 | 44 | VectorAccumulator::Driver(d.v, number_of_threads, 45 | VectorAccumulator::AtomicAccumulator); 46 | } 47 | } 48 | //----------------------------------------------------- 49 | 50 | static void BM_AtomicAccumulatorRelaxed(benchmark::State& state) { 51 | for (auto _ : state) { 52 | state.PauseTiming(); 53 | unsigned long number_of_threads = state.range(0); 54 | Data d(g_size); 55 | 56 | state.ResumeTiming(); 57 | 58 | VectorAccumulator::Driver(d.v, number_of_threads, 59 | VectorAccumulator::AtomicAccumulatorRelaxed); 60 | } 61 | } 62 | //----------------------------------------------------- 63 | 64 | static void BM_AtomicAccumulatorPartitionRelaxed(benchmark::State& state) { 65 | for (auto _ : state) { 66 | state.PauseTiming(); 67 | unsigned long number_of_threads = state.range(0); 68 | Data d(g_size); 69 | 70 | state.ResumeTiming(); 71 | 72 | VectorAccumulator::Driver( 73 | d.v, number_of_threads, 74 | VectorAccumulator::AtomicAccumulatorPartitionRelaxed); 75 | } 76 | } 77 | //----------------------------------------------------- 78 | static void BM_MutexAccumulatorPartition(benchmark::State& state) { 79 | for (auto _ : state) { 80 | state.PauseTiming(); 81 | unsigned long number_of_threads = state.range(0); 82 | Data d(g_size); 83 | 84 | state.ResumeTiming(); 85 | 86 | VectorAccumulator::Driver(d.v, number_of_threads, 87 | VectorAccumulator::MutexAccumulatorPartition); 88 | } 89 | } 90 | //----------------------------------------------------- 91 | 92 | // BENCHMARK(BM_AtomicAccumulator)->DenseRange(1, 12, 1); 93 | // BENCHMARK(BM_AtomicAccumulatorRelaxed)->DenseRange(1, 12, 1); 94 | // BENCHMARK(BM_AtomicAccumulatorPartitionRelaxed)->DenseRange(1, 12, 1); 95 | 96 | BENCHMARK(BM_MutexAccumulatorPartition)->DenseRange(1, 12, 1); 97 | BENCHMARK_MAIN(); 98 | 99 | -------------------------------------------------------------------------------- /src/main/atomic/vector_accumulator_benchmark.csv: -------------------------------------------------------------------------------- 1 | name,iterations,real_time,cpu_time,time_unit,bytes_per_second,items_per_second,label,error_occurred,error_message 2 | "BM_AtomicAccumulator/1",100,5.34434e+07,4.72814e+06,ns,,,,, 3 | "BM_AtomicAccumulator/2",100,1.2711e+08,4.31813e+06,ns,,,,, 4 | "BM_AtomicAccumulator/3",100,1.81731e+08,4.37247e+06,ns,,,,, 5 | "BM_AtomicAccumulator/4",100,1.83527e+08,4.39419e+06,ns,,,,, 6 | "BM_AtomicAccumulator/5",100,1.83589e+08,4.33095e+06,ns,,,,, 7 | "BM_AtomicAccumulator/6",100,1.91595e+08,4.37465e+06,ns,,,,, 8 | "BM_AtomicAccumulator/7",100,1.94796e+08,4.56426e+06,ns,,,,, 9 | "BM_AtomicAccumulator/8",100,1.92545e+08,4.56331e+06,ns,,,,, 10 | "BM_AtomicAccumulator/9",100,1.95476e+08,4.67013e+06,ns,,,,, 11 | "BM_AtomicAccumulator/10",100,1.94886e+08,4.66162e+06,ns,,,,, 12 | "BM_AtomicAccumulator/11",100,1.96212e+08,4.8152e+06,ns,,,,, 13 | "BM_AtomicAccumulator/12",130,1.98942e+08,5.11119e+06,ns,,,,, 14 | "BM_AtomicAccumulator/13",134,1.89634e+08,5.0129e+06,ns,,,,, 15 | "BM_AtomicAccumulator/14",137,1.95099e+08,5.06986e+06,ns,,,,, 16 | "BM_AtomicAccumulator/15",135,1.99903e+08,5.22428e+06,ns,,,,, 17 | "BM_AtomicAccumulatorImproved/1",125,9.69947e+06,5.06747e+06,ns,,,,, 18 | "BM_AtomicAccumulatorImproved/2",162,6.68875e+06,4.43246e+06,ns,,,,, 19 | "BM_AtomicAccumulatorImproved/3",161,5.92327e+06,4.3445e+06,ns,,,,, 20 | "BM_AtomicAccumulatorImproved/4",160,5.69184e+06,4.40201e+06,ns,,,,, 21 | "BM_AtomicAccumulatorImproved/5",159,5.63854e+06,4.44931e+06,ns,,,,, 22 | "BM_AtomicAccumulatorImproved/6",157,5.45128e+06,4.43524e+06,ns,,,,, 23 | "BM_AtomicAccumulatorImproved/7",157,5.30967e+06,4.42694e+06,ns,,,,, 24 | "BM_AtomicAccumulatorImproved/8",155,5.30205e+06,4.44463e+06,ns,,,,, 25 | "BM_AtomicAccumulatorImproved/9",156,5.48979e+06,4.50249e+06,ns,,,,, 26 | "BM_AtomicAccumulatorImproved/10",154,5.6694e+06,4.50371e+06,ns,,,,, 27 | "BM_AtomicAccumulatorImproved/11",154,5.81066e+06,4.56581e+06,ns,,,,, 28 | "BM_AtomicAccumulatorImproved/12",152,5.87415e+06,4.56189e+06,ns,,,,, 29 | "BM_AtomicAccumulatorImproved/13",151,6.13912e+06,4.63437e+06,ns,,,,, 30 | "BM_AtomicAccumulatorImproved/14",152,6.2298e+06,4.72053e+06,ns,,,,, 31 | "BM_AtomicAccumulatorImproved/15",149,6.05829e+06,4.69439e+06,ns,,,,, 32 | "BM_MutexAccumulator/1",158,8.46501e+06,4.43195e+06,ns,,,,, 33 | "BM_MutexAccumulator/2",160,6.53784e+06,4.35091e+06,ns,,,,, 34 | "BM_MutexAccumulator/3",161,5.92096e+06,4.35594e+06,ns,,,,, 35 | "BM_MutexAccumulator/4",161,5.66992e+06,4.39743e+06,ns,,,,, 36 | "BM_MutexAccumulator/5",158,5.607e+06,4.46168e+06,ns,,,,, 37 | "BM_MutexAccumulator/6",158,5.47809e+06,4.46318e+06,ns,,,,, 38 | "BM_MutexAccumulator/7",158,5.33997e+06,4.43911e+06,ns,,,,, 39 | "BM_MutexAccumulator/8",156,5.29067e+06,4.45219e+06,ns,,,,, 40 | "BM_MutexAccumulator/9",153,5.40923e+06,4.45903e+06,ns,,,,, 41 | "BM_MutexAccumulator/10",154,5.69097e+06,4.51355e+06,ns,,,,, 42 | "BM_MutexAccumulator/11",154,5.80286e+06,4.58338e+06,ns,,,,, 43 | "BM_MutexAccumulator/12",153,5.84177e+06,4.56235e+06,ns,,,,, 44 | "BM_MutexAccumulator/13",152,6.22925e+06,4.66695e+06,ns,,,,, 45 | "BM_MutexAccumulator/14",151,6.1554e+06,4.66008e+06,ns,,,,, 46 | "BM_MutexAccumulator/15",148,6.07242e+06,4.65803e+06,ns,,,,, 47 | -------------------------------------------------------------------------------- /src/main/conditional_variable/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_binary( 3 | name = "main", 4 | srcs = ["main.cc"], 5 | deps = [], 6 | ) 7 | 8 | cc_binary( 9 | name = "producer_consumer_conditional_var_main", 10 | srcs = ["producer_consumer_conditional_var_main.cc"], 11 | deps = [], 12 | ) 13 | 14 | cc_binary( 15 | name = "producer_consumer_lock_main", 16 | srcs = ["producer_consumer_lock_main.cc"], 17 | deps = [], 18 | ) 19 | 20 | cc_binary( 21 | name = "producer_consumer_conditional_var_simple", 22 | srcs = ["producer_consumer_conditional_var_simple.cc"], 23 | deps = ["//src/lib:csp_lib"], 24 | ) 25 | 26 | cc_binary( 27 | name = "conditional_var_csp_main", 28 | srcs = ["conditional_var_csp_main.cc"], 29 | deps = ["//src/lib:csp_lib"], 30 | ) 31 | 32 | cc_binary( 33 | name = "conditional_var_csp_sync_rcv_main", 34 | srcs = ["conditional_var_csp_sync_rcv_main.cc"], 35 | deps = ["//src/lib:csp_lib"], 36 | ) 37 | 38 | cc_binary( 39 | name = "conditional_var_csp_adder_main", 40 | srcs = ["conditional_var_csp_adder_main.cc"], 41 | deps = ["//src/lib:csp_lib"], 42 | ) 43 | 44 | cc_binary( 45 | name = "conditional_var_csp_adder_fork_join_main", 46 | srcs = ["conditional_var_csp_adder_fork_join_main.cc"], 47 | deps = ["//src/lib:csp_lib"], 48 | ) 49 | 50 | cc_binary( 51 | name = "dining_philosophore_main", 52 | srcs = ["dining_philosophore_main.cc"], 53 | deps = ["//src/lib:csp_lib"], 54 | ) 55 | -------------------------------------------------------------------------------- /src/main/conditional_variable/producer_consumer_conditional_var_main.cc: -------------------------------------------------------------------------------- 1 | // A demo of producer/Consumer using C++ conditional variable 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/conditional_variable:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/conditional_variable/{THIS_FILE_NAME}.cc -I ./ 10 | #include 11 | #include 12 | #include 13 | #include // For std::unique_lock 14 | #include 15 | #include 16 | #include 17 | 18 | // Create a function to generate a random value between 0 and 10 19 | auto GenRandomValue = std::bind(std::uniform_int_distribution<>(0, 10), 20 | std::default_random_engine()); 21 | 22 | std::mutex g_mutex; 23 | std::condition_variable g_cv; 24 | bool g_ready = false; 25 | int g_data = 0; 26 | void ConsumeData(int& data) {} 27 | 28 | void Consumer() { 29 | int data = 0; 30 | for (int i = 0; i < 100; i++) { 31 | std::unique_lock ul(g_mutex); 32 | 33 | // if blocked, ul.unlock() is automatically called. 34 | // if unblocked, ul.lock() is automatically called. 35 | g_cv.wait(ul, []() { return g_ready; }); 36 | // Sample data 37 | data = g_data; 38 | std::cout << "data: " << data << std::endl; 39 | g_ready = false; 40 | ul.unlock(); 41 | g_cv.notify_one(); 42 | ConsumeData(data); 43 | ul.lock(); 44 | } 45 | } 46 | 47 | void Producer() { 48 | for (int i = 0; i < 100; i++) { 49 | std::unique_lock ul(g_mutex); 50 | 51 | // Produce data 52 | g_data = GenRandomValue(); 53 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 54 | 55 | g_ready = true; 56 | ul.unlock(); 57 | g_cv.notify_one(); 58 | ul.lock(); 59 | g_cv.wait(ul, []() { return g_ready == false; }); 60 | } 61 | } 62 | 63 | int main() { 64 | std::thread t1(Consumer); 65 | std::thread t2(Producer); 66 | t1.join(); 67 | t2.join(); 68 | return 0; 69 | } -------------------------------------------------------------------------------- /src/main/conditional_variable/producer_consumer_conditional_var_simple.cc: -------------------------------------------------------------------------------- 1 | // A demo of producer/Consumer using C++ conditional variable 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/conditional_variable:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/conditional_variable/{THIS_FILE_NAME}.cc -I ./ 10 | // condition_variable example 11 | #include // std::condition_variable 12 | #include // std::cout 13 | #include // std::mutex, std::unique_lock 14 | #include // std::thread 15 | 16 | std::mutex mtx; 17 | std::condition_variable cv; 18 | bool ready = false; 19 | 20 | void print_id(int id) { 21 | std::unique_lock lck(mtx); 22 | while (!ready) { 23 | cv.wait(lck); 24 | } 25 | // ... 26 | std::cout << "thread " << id << '\n'; 27 | } 28 | 29 | void go() { 30 | std::unique_lock lck(mtx); 31 | ready = true; 32 | cv.notify_all(); 33 | } 34 | 35 | int main() { 36 | std::thread threads[10]; 37 | // spawn 10 threads: 38 | for (int i = 0; i < 10; ++i) { 39 | threads[i] = std::thread(print_id, i); 40 | } 41 | 42 | std::cout << "10 threads ready to race...\n"; 43 | go(); // go! 44 | 45 | for (auto& th : threads) { 46 | th.join(); 47 | } 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /src/main/conditional_variable/producer_consumer_lock_main.cc: -------------------------------------------------------------------------------- 1 | // A demo of producer/consumer using busy waiting on shared memory 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/conditional_variable:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/conditional_variable/{THIS_FILE_NAME}.cc -I ./ 10 | #include // std::condition_variable, std::cv_status 11 | #include 12 | #include // For std::unique_lock 13 | #include 14 | #include 15 | 16 | // Create a function to generate a random value between 0 and 10 17 | auto GenRandomValue = std::bind(std::uniform_int_distribution<>(0, 10), 18 | std::default_random_engine()); 19 | 20 | std::mutex g_mutex; 21 | bool g_ready = false; 22 | int g_data = 0; 23 | 24 | /** 25 | * Uses busy waiting on g_ready to get data from producer 26 | */ 27 | void consumer() { 28 | int receivedCount = 0; 29 | 30 | for (int i = 0; i < 100; i++) { 31 | std::unique_lock ul(g_mutex); 32 | // Busy waiting 33 | while (!g_ready) { 34 | ul.unlock(); 35 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 36 | ul.lock(); 37 | } 38 | 39 | std::cout << "got g_data: " << g_data << std::endl; 40 | g_ready = false; 41 | } 42 | } 43 | 44 | /** 45 | * Produces data and then sets g_ready to true 46 | */ 47 | void producer() { 48 | for (int i = 0; i < 100; i++) { 49 | std::unique_lock ul(g_mutex); 50 | 51 | // Produce data 52 | g_data = GenRandomValue(); 53 | 54 | // Announce that data is produced 55 | g_ready = true; 56 | 57 | ul.unlock(); 58 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 59 | ul.lock(); 60 | } 61 | } 62 | 63 | // This example doesn't have conditional variables. 64 | int main() { 65 | std::thread t1(consumer); 66 | std::thread t2(producer); 67 | t1.join(); 68 | t2.join(); 69 | return 0; 70 | } -------------------------------------------------------------------------------- /src/main/exceptions/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_binary( 3 | name = "basic_exception", 4 | srcs = ["basic_exception.cc"], 5 | copts = [ 6 | "-std=c++23", 7 | ], 8 | deps = [ 9 | "//src/lib:logger", 10 | ], 11 | ) 12 | 13 | cc_binary( 14 | name = "future_promise_thread", 15 | srcs = ["future_promise_thread.cc"], 16 | copts = [ 17 | "-std=c++23", 18 | ], 19 | deps = [ 20 | "//src/lib:logger", 21 | ], 22 | ) 23 | 24 | cc_binary( 25 | name = "future_promise_jthread", 26 | srcs = ["future_promise_jthread.cc"], 27 | copts = [ 28 | "-std=c++23", 29 | ], 30 | deps = [ 31 | "//src/lib:logger", 32 | ], 33 | ) 34 | 35 | cc_binary( 36 | name = "exception_ptr", 37 | srcs = ["exception_ptr.cc"], 38 | copts = [ 39 | "-std=c++23", 40 | ], 41 | deps = [ 42 | "//src/lib:logger", 43 | ], 44 | ) 45 | 46 | cc_binary( 47 | name = "central_queue", 48 | srcs = ["central_queue.cc"], 49 | copts = [ 50 | "-std=c++23", 51 | ], 52 | deps = [ 53 | "//src/lib:logger", 54 | ], 55 | ) 56 | 57 | cc_binary( 58 | name = "safe_locks", 59 | srcs = ["safe_locks.cc"], 60 | copts = [ 61 | "-std=c++23", 62 | ], 63 | deps = [ 64 | "//src/lib:logger", 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /src/main/exceptions/basic_exception.cc: -------------------------------------------------------------------------------- 1 | #include "src/lib/logger.h" 2 | #include 3 | #include 4 | 5 | void level3() { 6 | Logger l3("level3"); 7 | throw std::runtime_error("Exception in level3"); 8 | } 9 | 10 | void level2() { 11 | Logger l2("level2"); 12 | level3(); 13 | } 14 | 15 | void level1() { 16 | Logger l1("level1"); 17 | level2(); 18 | } 19 | 20 | int main() { 21 | try { 22 | level1(); 23 | } catch (const std::exception &e) { 24 | std::cout << "Caught: " << e.what() << "\n"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/exceptions/central_queue.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class ExceptionChannel { 11 | std::queue q; 12 | std::mutex mtx; 13 | 14 | public: 15 | void push(std::exception_ptr e) { 16 | std::lock_guard lock(mtx); 17 | q.push(e); 18 | } 19 | 20 | std::optional pop() { 21 | std::lock_guard lock(mtx); 22 | if (q.empty()) 23 | return std::nullopt; 24 | auto e = q.front(); 25 | q.pop(); 26 | return e; 27 | } 28 | 29 | bool empty() { 30 | std::lock_guard lock(mtx); 31 | return q.empty(); 32 | } 33 | }; 34 | 35 | void worker(int id, ExceptionChannel &channel) { 36 | try { 37 | if (id % 2 == 0) { 38 | throw std::runtime_error("Failure from thread " + std::to_string(id)); 39 | } 40 | } catch (...) { 41 | channel.push(std::current_exception()); 42 | } 43 | } 44 | 45 | int main() { 46 | ExceptionChannel channel; 47 | 48 | { 49 | std::vector jthreads; 50 | 51 | for (int i = 0; i < 10; ++i) { 52 | jthreads.emplace_back(worker, i, std::ref(channel)); 53 | } 54 | } 55 | 56 | while (auto eptr = channel.pop()) { // pop() returns nullopt when empty 57 | try { 58 | std::rethrow_exception(*eptr); 59 | } catch (const std::exception &e) { 60 | std::cout << "Caught: " << e.what() << '\n'; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/exceptions/exception_ptr.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void worker(std::exception_ptr &eptr) { 9 | try { 10 | throw std::runtime_error("Something failed in worker"); 11 | } catch (...) { 12 | eptr = std::current_exception(); 13 | } 14 | } 15 | 16 | int main() { 17 | std::exception_ptr eptr; 18 | std::thread t(worker, std::ref(eptr)); 19 | t.join(); 20 | 21 | if (eptr) { 22 | try { 23 | std::rethrow_exception(eptr); 24 | } catch (const std::exception &e) { 25 | std::cerr << "Caught from worker: " << e.what() << std::endl; 26 | } 27 | } 28 | 29 | int sum = std::ranges::fold_left(values, 0, std::plus{}); 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /src/main/exceptions/exception_ptr_vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::vector exceptions; 7 | 8 | void worker(int id) { 9 | try { 10 | if (id == 1) 11 | throw std::runtime_error("Worker failed"); 12 | } catch (...) { 13 | exceptions[id] = std::current_exception(); 14 | } 15 | } 16 | 17 | int main() { 18 | int n = 3; 19 | exceptions.resize(n); 20 | std::vector threads; 21 | 22 | for (int i = 0; i < n; ++i) 23 | threads.emplace_back(worker, i); 24 | 25 | for (auto &t : threads) 26 | t.join(); 27 | 28 | for (int i = 0; i < n; ++i) { 29 | if (exceptions[i]) { 30 | try { 31 | std::rethrow_exception(exceptions[i]); 32 | } catch (const std::exception &e) { 33 | std::cout << "Thread " << i << ": " << e.what() << '\n'; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/exceptions/future_promise_jthread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main() { 13 | std::promise p; 14 | std::future f = p.get_future(); 15 | bool throw_error = true; // Toggle this to test both paths 16 | 17 | std::jthread t([&]() { 18 | try { 19 | if (throw_error) { 20 | throw std::runtime_error("Runtime exception!"); 21 | } 22 | p.set_value(); // signal success 23 | } catch (...) { 24 | p.set_exception(std::current_exception()); // signal exception 25 | } 26 | }); 27 | 28 | try { 29 | f.get(); // rethrows exception if set_exception was used 30 | std::cout << "Thread finished without exception.\n"; 31 | } catch (const std::exception &e) { 32 | std::cout << "Caught: " << e.what() << std::endl; 33 | } 34 | 35 | // No need to call t.join(); jthread auto-joins 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/exceptions/future_promise_thread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void worker(std::promise p, bool throw_error) { 7 | try { 8 | if (throw_error) { 9 | throw std::runtime_error("Runtime exception!"); 10 | } 11 | p.set_value(); // signal success 12 | } catch (...) { 13 | p.set_exception(std::current_exception()); // signal exception 14 | } 15 | } 16 | 17 | int main() { 18 | std::promise p; 19 | std::future f = p.get_future(); 20 | bool throw_error = true; // Toggle this to test both paths 21 | 22 | std::thread t(worker, std::move(p), throw_error); 23 | 24 | try { 25 | f.get(); // rethrows exception if set_exception was used 26 | std::cout << "Thread finished without exception.\n"; 27 | } catch (const std::exception &e) { 28 | std::cout << "Caught: " << e.what() << std::endl; 29 | } 30 | 31 | t.join(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/exceptions/jthread_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | std::jthread jt([](std::stop_token st) { 8 | try { 9 | if (!st.stop_requested()) { 10 | throw std::runtime_error("Exception in jthread"); 11 | } 12 | } catch (const std::exception& e) { 13 | std::cerr << "Caught in jthread: " << e.what() << '\n'; 14 | } 15 | }); 16 | // jt is auto-joined on destruction 17 | return 0; 18 | } -------------------------------------------------------------------------------- /src/main/exceptions/raii.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import matplotlib.patches as patches 3 | 4 | fig, ax = plt.subplots(figsize=(12, 3)) 5 | 6 | # Define phases 7 | phases = ['Object Created', 'Resource Acquired', 'Work Done', 'Object Exits Scope', 'Resource Released'] 8 | colors = ['lightblue', 'lightgreen', 'lightyellow', 'salmon', 'lightcoral'] 9 | positions = [0, 2.5, 5, 7.5, 10] 10 | 11 | # Draw rectangles with dotted outline 12 | for i, (pos, phase) in enumerate(zip(positions, phases)): 13 | rect = patches.FancyBboxPatch((pos, 0), 2.1, 1.1, 14 | boxstyle="round,pad=0.1", 15 | facecolor=colors[i], 16 | edgecolor='black', 17 | linewidth=1, 18 | linestyle='dotted') 19 | ax.add_patch(rect) 20 | ax.text(pos + 1.1, 0.6, phase, ha='center', va='center', fontsize=14,) 21 | 22 | # Draw arrows between stages 23 | for i in range(len(positions) - 1): 24 | ax.annotate('', xy=(positions[i+1], 0.6), xytext=(positions[i] + 2.2, 0.6), 25 | arrowprops=dict(arrowstyle="->", lw=3, color='black')) 26 | 27 | # Annotate object creation start 28 | ax.annotate('Object Construction Begins', 29 | xy=(0 + 1.1, 1.3), xytext=(0 + 1.1, 1.9), 30 | ha='center', fontsize=14, color='blue', 31 | arrowprops=dict(arrowstyle="-[", lw=2, color='blue')) 32 | 33 | # Annotate constructor (resource acquired) 34 | ax.annotate('Constructor Body', 35 | xy=(2.5 + 1.1, 1.3), xytext=(2.5 + 1.1, 2.6), 36 | ha='center', fontsize=14, color='green', 37 | arrowprops=dict(arrowstyle="-[", lw=2, color='green')) 38 | 39 | # Annotate destructor 40 | ax.annotate('Destructor Body', 41 | xy=(10 + 1.1, 1.3), xytext=(10 + 1.1, 1.9), 42 | ha='center', fontsize=14, color='red', 43 | arrowprops=dict(arrowstyle="-[", lw=2, color='red')) 44 | 45 | # Formatting 46 | ax.set_xlim(-0.5, 13) 47 | ax.set_ylim(-0.5, 2.2) 48 | ax.axis('off') 49 | # ax.set_title('RAII Lifecycle: Scope-Based Resource Management', fontsize=16) 50 | 51 | plt.tight_layout() 52 | plt.show() 53 | -------------------------------------------------------------------------------- /src/main/exceptions/safe_locks.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::mutex mtx; 6 | 7 | void worker() { 8 | try { 9 | throw std::runtime_error("Boom in thread!"); 10 | } catch (const std::exception &e) { 11 | std::cerr << "Caught in thread: " << e.what() << '\n'; 12 | } 13 | } 14 | void critical_section() { 15 | std::lock_guard lock(mtx); 16 | std::cout << "Doing something critical..." << std::endl; 17 | throw std::runtime_error("Error during critical section"); 18 | } 19 | 20 | int main() { 21 | try { 22 | critical_section(); 23 | } catch (const std::exception &e) { 24 | std::cerr << "Caught: " << e.what() << std::endl; 25 | } 26 | return 0; 27 | } -------------------------------------------------------------------------------- /src/main/exceptions/thread_handling_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::thread t([] { 7 | try { 8 | throw std::runtime_error("Boom in thread!"); 9 | } catch (const std::exception &e) { 10 | std::cerr << "Caught in thread: " << e.what() << '\n'; 11 | } 12 | }); 13 | t.join(); 14 | std::cout << "Main thread continues safely\n"; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/exceptions/thread_not_handling_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::thread t([] { 7 | // This will terminate the program 8 | throw std::runtime_error("Uncaught exception in thread"); 9 | }); 10 | t.join(); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /src/main/functors_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "src/lib/utility.h" 7 | 8 | // A demo for creating two threads 9 | // Run this using one of the following methods: 10 | // 1. With bazel: bazel run src/main:{THIS_FILE_NAME_WITHOUT_EXTENSION} 11 | // 2. With plain g++: g++ -std=c++17 -lpthread 12 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 13 | int main() { 14 | const int number_of_threads = 10; 15 | uint64_t number_of_elements = 1000 * 1000 * 1000; 16 | uint64_t step = number_of_elements / number_of_threads; 17 | std::vector threads; 18 | 19 | std::vector functors; 20 | 21 | for (int i = 0; i < number_of_threads; i++) { 22 | AccumulateFunctor *functor = new AccumulateFunctor(); 23 | threads.push_back( 24 | std::thread(std::ref(*functor), i * step, (i + 1) * step)); 25 | // It would cause incorrect result if we added a functor instead of a 26 | // pointer to it. 27 | functors.push_back(functor); 28 | } 29 | 30 | for (std::thread &t : threads) { 31 | if (t.joinable()) { 32 | t.join(); 33 | } 34 | } 35 | 36 | int64_t total = 0; 37 | for (auto pf : functors) { 38 | total += pf->_sum; 39 | } 40 | 41 | for (auto &pf : functors) { 42 | delete pf; 43 | } 44 | 45 | std::cout << "total: " << total << std::endl; 46 | 47 | return 0; 48 | } -------------------------------------------------------------------------------- /src/main/functors_main_unique_ptr.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "src/lib/utility.h" 7 | 8 | // A demo for creating two threads 9 | // Run this using one of the following methods: 10 | // 1. With bazel: bazel run src/main:{THIS_FILE_NAME_WITHOUT_EXTENSION} 11 | // 2. With plain g++: g++ -std=c++17 -lpthread 12 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 13 | int main() { 14 | const int number_of_threads = 10; 15 | uint64_t number_of_elements = 1000 * 1000 * 1000; 16 | uint64_t step = number_of_elements / number_of_threads; 17 | std::vector threads; 18 | 19 | std::vector> functors; 20 | 21 | for (int i = 0; i < number_of_threads; i++) { 22 | // Using a unique pointer to avoid memory leak. 23 | std::unique_ptr functor(new AccumulateFunctor()); 24 | threads.push_back( 25 | std::thread(std::ref(*functor), i * step, (i + 1) * step)); 26 | // It would cause incorrect result if we added a functor instead of a 27 | // pointer to it. 28 | functors.push_back(std::move(functor)); 29 | } 30 | 31 | for (std::thread &t : threads) { 32 | if (t.joinable()) { 33 | t.join(); 34 | } 35 | } 36 | 37 | int64_t total = 0; 38 | for (auto &pf : functors) { 39 | total += pf->_sum; 40 | } 41 | std::cout << "total: " << total << std::endl; 42 | 43 | return 0; 44 | } -------------------------------------------------------------------------------- /src/main/jthread/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_test( 3 | name = "jthread", 4 | srcs = ["jthread.cc"], 5 | copts = [ 6 | "-std=c++23", 7 | ], 8 | deps = [ 9 | "//src/lib:utility_lib", 10 | "@googletest//:gtest_main", 11 | ], 12 | ) 13 | 14 | cc_binary( 15 | name = "good_jthread_stop_with_callback", 16 | srcs = ["good_jthread_stop_with_callback.cc"], 17 | copts = [ 18 | "-std=c++23", 19 | ], 20 | deps = [ 21 | "//src/lib:utility_lib", 22 | # "@googletest//:gtest_main", 23 | ], 24 | ) 25 | 26 | cc_binary( 27 | name = "better_jthread_stop_with_callback", 28 | srcs = ["better_jthread_stop_with_callback.cc"], 29 | copts = [ 30 | "-std=c++23", 31 | ], 32 | deps = [ 33 | "//src/lib:utility_lib", 34 | # "@googletest//:gtest_main", 35 | ], 36 | ) 37 | 38 | cc_binary( 39 | name = "better_jthread_stop_with_callback2", 40 | srcs = ["better_jthread_stop_with_callback2.cc"], 41 | copts = [ 42 | "-std=c++23", 43 | ], 44 | deps = [ 45 | "//src/lib:utility_lib", 46 | # "@googletest//:gtest_main", 47 | ], 48 | ) 49 | 50 | cc_binary( 51 | name = "bad_thread_stop_1", 52 | srcs = ["bad_thread_stop_2.cc"], 53 | copts = [ 54 | "-std=c++23", 55 | ], 56 | deps = [ 57 | "//src/lib:utility_lib", 58 | # "@googletest//:gtest_main", 59 | ], 60 | ) 61 | 62 | cc_binary( 63 | name = "bad_thread_stop_2", 64 | srcs = ["bad_thread_stop_2.cc"], 65 | copts = [ 66 | "-std=c++23", 67 | ], 68 | deps = [ 69 | "//src/lib:utility_lib", 70 | # "@googletest//:gtest_main", 71 | ], 72 | ) 73 | 74 | cc_binary( 75 | name = "bad_thread_stop_with_callback", 76 | srcs = ["bad_thread_stop_with_callback.cc"], 77 | copts = [ 78 | "-std=c++23", 79 | ], 80 | deps = [ 81 | "//src/lib:utility_lib", 82 | # "@googletest//:gtest_main", 83 | ], 84 | ) 85 | 86 | cc_binary( 87 | name = "good_jthread_stop_1", 88 | srcs = ["good_jthread_stop_1.cc"], 89 | copts = [ 90 | "-std=c++23", 91 | ], 92 | deps = [ 93 | "//src/lib:utility_lib", 94 | # "@googletest//:gtest_main", 95 | ], 96 | ) 97 | 98 | cc_binary( 99 | name = "good_jthread_stop_2", 100 | srcs = ["good_jthread_stop_2.cc"], 101 | copts = [ 102 | "-std=c++23", 103 | ], 104 | deps = [ 105 | "//src/lib:utility_lib", 106 | # "@googletest//:gtest_main", 107 | ], 108 | ) 109 | 110 | cc_binary( 111 | name = "lambda_jthread_main", 112 | srcs = ["lambda_jthread_main.cc"], 113 | # copts = [ 114 | # "-std=c++20", 115 | # "-stdlib=libc++", 116 | # ], 117 | deps = ["//src/lib:utility_lib"], 118 | ) 119 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread1.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void worker() { 5 | std::cout << "Worker thread running\n"; 6 | } 7 | 8 | void doSomethingElse() { 9 | std::cout << "Main thread doing other work\n"; 10 | } 11 | 12 | int problematicThread() { 13 | { 14 | std::thread t(worker); 15 | // Oops! t goes out of scope without join() or detach() 16 | } 17 | // Program terminates with std::terminate() 18 | doSomethingElse(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void worker() { std::cout << "Worker thread running\n"; } 5 | 6 | void doSomethingElse() { std::cout << "Main thread doing other work\n"; } 7 | 8 | void problematicThread() { 9 | std::thread t(worker); 10 | // Simulate an exception before calling t.join() 11 | throw std::runtime_error("Unexpected error!"); 12 | t.join(); // Never reached → std::terminate() called 13 | doSomethingElse(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread_stop_1.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic stop_flag{false}; 6 | void do_work() { 7 | // Simulate some work 8 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 9 | } 10 | void worker() { 11 | while (!stop_flag) { 12 | do_work(); 13 | } 14 | } 15 | 16 | int main() { 17 | std::thread t(worker); 18 | std::this_thread::sleep_for(std::chrono::seconds(1)); 19 | stop_flag = true; // manually signal to stop 20 | t.join(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread_stop_2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void do_work() { 7 | // Simulate some work 8 | std::cout << "Doing work..." << std::endl; 9 | } 10 | 11 | void worker(std::future stop_signal) { 12 | while (stop_signal.wait_for(std::chrono::milliseconds(1000)) == 13 | std::future_status::timeout) { 14 | do_work(); 15 | } 16 | } 17 | 18 | int main() { 19 | std::promise exit_signal; 20 | std::thread t(worker, exit_signal.get_future()); 21 | 22 | std::this_thread::sleep_for(std::chrono::seconds(10)); 23 | exit_signal.set_value(); // request stop 24 | t.join(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread_stop_3.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::mutex mtx; 8 | std::condition_variable cv; 9 | bool stop_requested = false; 10 | 11 | void worker() { 12 | std::unique_lock lock(mtx); 13 | while (!stop_requested) { 14 | cv.wait_for(lock, std::chrono::milliseconds(100)); // periodic wake-up 15 | do_work(); 16 | } 17 | std::cout << "Worker thread exiting\n"; 18 | } 19 | 20 | int main() { 21 | std::thread t(worker); 22 | 23 | std::this_thread::sleep_for(std::chrono::seconds(1)); 24 | { 25 | std::lock_guard lock(mtx); 26 | stop_requested = true; 27 | } 28 | cv.notify_one(); // wake up thread to exit 29 | 30 | t.join(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/jthread/bad_thread_stop_with_callback.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | std::condition_variable cv; 9 | std::mutex mtx; 10 | bool data_ready = false; 11 | bool done = false; 12 | 13 | void consumer() { 14 | std::unique_lock lock(mtx); 15 | while (!done) { 16 | cv.wait(lock, [&] { return data_ready || done; }); 17 | 18 | if (data_ready) { 19 | std::cout << "Consumer processed data\n"; 20 | data_ready = false; 21 | } 22 | } 23 | } 24 | 25 | void producer() { 26 | for (int _ : std::views::iota(0, 5)) { 27 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 28 | { 29 | std::lock_guard lock(mtx); 30 | data_ready = true; 31 | // if (i == 4) { 32 | // done = true; // signal completion 33 | // } 34 | } 35 | cv.notify_one(); 36 | } 37 | } 38 | 39 | int main() { 40 | std::thread consumer_thread(consumer); 41 | std::thread producer_thread(producer); 42 | std::this_thread::sleep_for(std::chrono::seconds(2)); 43 | producer_thread.join(); 44 | consumer_thread.join(); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/jthread/better_jthread_stop_with_callback.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // This version uses a future and promise to not only signal the completion but 10 | // also propagate exceptions from the producer to the main thread. 11 | 12 | std::condition_variable cv; 13 | std::mutex mtx; 14 | bool data_ready{false}; 15 | 16 | void consumer(std::stop_token st) { 17 | std::stop_callback on_stop(st, [] { 18 | std::cout << "Stop requested, waking consumer\n"; 19 | cv.notify_one(); 20 | }); 21 | 22 | std::unique_lock lock(mtx); 23 | while (!done) { 24 | cv.wait(lock, [&] { return st.stop_requested() || data_ready; }); 25 | if (st.stop_requested()) { 26 | std::cout << "Consumer exiting due to stop\n"; 27 | return; 28 | } 29 | 30 | if (data_ready) { 31 | std::cout << "Consumer processed data\n"; 32 | data_ready = false; 33 | } 34 | } 35 | } 36 | 37 | void producer(std::promise &done_promise) { 38 | try { 39 | std::this_thread::sleep_for(std::chrono::seconds(1)); 40 | throw std::runtime_error("Producer error!"); 41 | 42 | { 43 | std::lock_guard lock(mtx); 44 | data_ready = true; 45 | cv.notify_one(); 46 | } 47 | done_promise.set_value(); 48 | } catch (...) { 49 | done_promise.set_exception(std::current_exception()); 50 | } 51 | } 52 | 53 | int main() { 54 | std::promise producer_done; 55 | std::future done_future = producer_done.get_future(); 56 | 57 | std::jthread consumer_thread(consumer); 58 | std::jthread producer_thread(producer, std::ref(producer_done)); 59 | 60 | try { 61 | done_future.get(); // ✅ blocks and rethrows exception if any 62 | } catch (const std::exception &ex) { 63 | std::cout << "Caught exception from producer: " << ex.what() << '\n'; 64 | consumer_thread.request_stop(); 65 | std::cout << "Consumer thread requested to stop\n"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/jthread/better_jthread_stop_with_callback2.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * This version uses a future and promise to not only signal the completion but 3 | * also propagate exceptions from the producer to the main thread. 4 | * Also, it uses a shared state instead of global variables. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct SharedState { 15 | std::condition_variable cv; 16 | std::mutex mtx; 17 | bool data_ready{false}; 18 | bool done{false}; 19 | }; 20 | 21 | /** 22 | * Consumer function that waits for data to be ready or for a stop request. 23 | * Note: jthread expects the first parameter to be the stop_token. 24 | */ 25 | void consumer(std::stop_token st, SharedState &state) { 26 | std::stop_callback on_stop(st, [&] { 27 | std::cout << "Stop requested, waking consumer\n"; 28 | state.cv.notify_one(); 29 | }); 30 | 31 | std::unique_lock lock(state.mtx); 32 | while (!state.done) { 33 | state.cv.wait(lock, 34 | [&] { return st.stop_requested() || state.data_ready; }); 35 | if (st.stop_requested()) { 36 | std::cout << "Consumer exiting due to stop\n"; 37 | return; 38 | } 39 | 40 | if (state.data_ready) { 41 | std::cout << "Consumer processed data\n"; 42 | state.data_ready = false; 43 | } 44 | } 45 | } 46 | 47 | void producer(SharedState &state) { 48 | for (int _ : std::views::iota(0, 5)) { 49 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 50 | { 51 | std::lock_guard lock(state.mtx); 52 | state.data_ready = true; 53 | if (i == 4) { 54 | done = true; // signal completion 55 | } 56 | } 57 | state.cv.notify_one(); 58 | } 59 | } 60 | 61 | int main() { 62 | SharedState shared; 63 | std::jthread consumer_thread(consumer, std::ref(shared)); 64 | std::jthread producer_thread(producer, std::ref(shared)); 65 | std::this_thread::sleep_for(std::chrono::seconds(2)); 66 | std::cout << "Main thread exiting\n"; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/jthread/cppThreadEvolution.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | # Enhanced Data with C++14 and C++26 (future) 5 | years = [2011, 2014, 2017, 2020, 2023, 2026] 6 | labels = [ 7 | "C++11\nFundamental Multithreading Introduced\n(std::thread, mutexes, condition_variable, atomic,\nasync, future, promise)", 8 | "C++14\nConcurrency Refinements\n(shared_timed_mutex, chrono literals e.g., 100ms)", 9 | "C++17\nEnhanced Synchronization & Parallelism\n(scoped_lock, shared_mutex, parallel algorithms\nwith std::execution policies)", 10 | "C++20\nMajor Concurrency Additions\n(Coroutines [co_await, co_yield], jthread & stop_token,\natomic_ref, barrier, latch, counting_semaphore)", 11 | "C++23\natomic enhancements (floating-point, trivially copyable structs),\nsync_wait (coroutine/future sync), expected, stacktrace", 12 | "C++26\nPlanned Advanced Features (proposals)\n(Hazard Pointers, RCU, Executor model, Networking TS)", 13 | ] 14 | colors = ["#4c72b0", "#937860", "#55a868", "#c44e52", "#8c564b", "#e377c2"] 15 | y_offsets = [2.5, 1.75, 2.5, 1.75, 2.5, 1.75] 16 | 17 | # Plot 18 | fig, ax = plt.subplots(figsize=(18, 7)) 19 | ax.set_xlim(2009, 2028) 20 | ax.set_ylim(0, 3) 21 | ax.axis("off") 22 | ax.set_facecolor("#f9f9f9") 23 | 24 | # Wavy timeline curve 25 | x_curve = np.linspace(2010, 2027, 500) 26 | y_curve = 1 + 0.05 * np.sin(0.6 * np.pi * (x_curve - 2010)) 27 | ax.plot(x_curve, y_curve, color="black", linewidth=3) 28 | 29 | # Y position for each milestone on curve 30 | y_positions = 1 + 0.05 * np.sin(0.6 * np.pi * (np.array(years) - 2010)) 31 | 32 | # Milestones + connector lines 33 | for year, label, color, y_offset, y_base in zip(years, labels, colors, y_offsets, y_positions): 34 | ax.vlines( 35 | x=year, 36 | ymin=y_base, 37 | ymax=y_offset - 0.45, 38 | color=color, 39 | linestyle="dotted", 40 | linewidth=2, 41 | ) 42 | ax.plot( 43 | year, 44 | y_base, 45 | "o", 46 | markersize=14, 47 | color=color, 48 | markeredgecolor="black", 49 | markeredgewidth=1.5, 50 | ) 51 | ax.text( 52 | year, 53 | y_offset + 0.05, 54 | f"{int(year)}", 55 | ha="center", 56 | va="bottom", 57 | fontsize=14, 58 | weight="bold", 59 | color="#222222", 60 | ) 61 | ax.text( 62 | year, 63 | y_offset - 0.02, 64 | label, 65 | ha="center", 66 | va="top", 67 | fontsize=11, 68 | wrap=True, 69 | color="#222222", 70 | ) 71 | 72 | plt.tight_layout() 73 | plt.show() 74 | 75 | fig.savefig("cpp_multithreading_timeline_wavy.png", dpi=300, bbox_inches="tight") 76 | -------------------------------------------------------------------------------- /src/main/jthread/good_jthread1.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | void worker() { std::cout << "Worker thread running\n"; } 4 | 5 | void doSomethingElse() { std::cout << "Main thread doing other work\n"; } 6 | 7 | int safeJthread() { 8 | { 9 | std::jthread t(worker); 10 | // Safe: jthread auto-joins on destruction 11 | } 12 | 13 | // Program continues safely 14 | doSomethingElse(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/jthread/good_jthread2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void worker() { std::cout << "Worker thread running\n"; } 6 | 7 | void doSomethingElse() { std::cout << "Main thread doing other work\n"; } 8 | 9 | int safeJthread() { 10 | std::jthread t(worker); 11 | 12 | // Exception occurs 13 | throw std::runtime_error("Unexpected error!"); 14 | 15 | doSomethingElse(); // Never reached, but no crash 16 | } 17 | -------------------------------------------------------------------------------- /src/main/jthread/good_jthread_stop_1.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void do_work() { 5 | // Simulate some work 6 | std::cout << "Doing work..." << std::endl; 7 | } 8 | 9 | void worker(std::stop_token st) { 10 | while (!st.stop_requested()) { 11 | do_work(); // your work function 12 | } 13 | std::cout << "Worker thread exiting\n"; 14 | } 15 | 16 | int main() { 17 | std::jthread t(worker); // passes stop_token to worker 18 | 19 | std::this_thread::sleep_for(std::chrono::seconds(1)); 20 | 21 | t.request_stop(); // cooperative cancellation request 22 | // automatic join when t goes out of scope 23 | } 24 | -------------------------------------------------------------------------------- /src/main/jthread/good_jthread_stop_2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::mutex mtx; 7 | std::condition_variable cv; 8 | 9 | void do_work() { 10 | std::cout << "Working...\n"; 11 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 12 | } 13 | 14 | void worker(std::stop_token st) { 15 | std::unique_lock lock(mtx); 16 | while (!st.stop_requested()) { 17 | do_work(); 18 | 19 | // Block for 500ms or until stop is requested 20 | cv.wait_for(lock, std::chrono::milliseconds(500), 21 | [&] { return st.stop_requested(); }); 22 | } 23 | 24 | std::cout << "Worker thread exiting\n"; 25 | } 26 | 27 | int main() { 28 | std::jthread t(worker); // passes stop_token automatically 29 | 30 | std::this_thread::sleep_for(std::chrono::seconds(2)); 31 | t.request_stop(); // cooperative cancellation 32 | } 33 | -------------------------------------------------------------------------------- /src/main/jthread/good_jthread_stop_with_callback.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::condition_variable cv; 10 | std::mutex mtx; 11 | bool data_ready{false}; 12 | bool done{false}; 13 | 14 | void consumer(std::stop_token st) { 15 | std::stop_callback on_stop(st, [] { 16 | std::cout << "Stop requested, waking consumer\n"; 17 | cv.notify_one(); 18 | }); 19 | 20 | std::unique_lock lock(mtx); 21 | while (!done) { 22 | cv.wait(lock, [&] { return st.stop_requested() || data_ready; }); 23 | if (st.stop_requested()) { 24 | std::cout << "Consumer exiting due to stop\n"; 25 | return; 26 | } 27 | 28 | if (data_ready) { 29 | std::cout << "Consumer processed data\n"; 30 | data_ready = false; 31 | } 32 | } 33 | } 34 | 35 | void producer() { 36 | for (int _ : std::views::iota(0, 5)) { 37 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 38 | { 39 | std::lock_guard lock(mtx); 40 | data_ready = true; 41 | // if (i == 4) { 42 | // done = true; // signal completion 43 | // } 44 | } 45 | cv.notify_one(); 46 | } 47 | } 48 | 49 | int main() { 50 | 51 | std::jthread consumer_thread(consumer); 52 | std::jthread producer_thread(producer); 53 | std::this_thread::sleep_for(std::chrono::seconds(2)); 54 | std::cout << "Main thread exiting\n"; 55 | } -------------------------------------------------------------------------------- /src/main/jthread/jthread.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * A demo for creating jthreads. 3 | * The code calculates the sum of integers from 0 to total_elements - 1 using 4 | * multiple threads. Each thread computes a partial sum of a range of numbers, 5 | * and the results are combined to get the total sum. 6 | * 7 | * Note that this code uses C++23 features and it contains the GTEST unit tests. 8 | * Run this using one of the following methods: 9 | * 1. With bazel: bazel test //src/main/jthread 10 | * 2. With plain g++: g++ -std=c++23 -lpthread 11 | * /Users/ari/github/multithreading_cpp/src/main/jthread/{THIS_FILE_NAME}.cc -I 12 | * ./ 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** 25 | * Calculates the sum of a range of numbers using multiple threads. 26 | * Each thread calculates the sum of a portion of the range. 27 | * The results are then combined to get the total sum. 28 | */ 29 | void compute_partial_sum(std::size_t num_threads, std::size_t thread_index, 30 | std::size_t total_elements, 31 | std::vector &partial_sums) { 32 | // Calculate the size of each chunk 33 | const auto chunk_size = total_elements / num_threads; 34 | 35 | // Determine the start and end indices for this thread's range 36 | const auto start_index = thread_index * chunk_size; 37 | const auto end_index = 38 | (thread_index == num_threads - 1) 39 | ? total_elements // Last thread handles the remainder 40 | : start_index + chunk_size; 41 | 42 | // Generate the range of numbers for this thread 43 | auto range = std::views::iota(start_index, end_index); 44 | 45 | // Compute the sum of the range and store it in the partial_sums vector 46 | partial_sums[thread_index] = 47 | std::ranges::fold_left(range, std::uint64_t{0}, std::plus{}); 48 | } 49 | 50 | /** 51 | * Computes the total sum of integers from 0 to total_elements - 1 52 | * using multiple threads. 53 | */ 54 | std::uint64_t compute_total_sum(std::size_t num_threads, 55 | std::size_t total_elements) { 56 | if (num_threads == 0) { 57 | throw std::invalid_argument("Number of threads must be greater than 0"); 58 | } 59 | 60 | std::vector partial_sums(num_threads); 61 | 62 | // Launch threads to compute partial sums 63 | { 64 | std::vector threads; 65 | for (auto i : std::views::iota(0uz, num_threads)) { 66 | threads.emplace_back(compute_partial_sum, num_threads, i, total_elements, 67 | std::ref(partial_sums)); 68 | } 69 | } // jthreads auto-join here 70 | 71 | // Return the total sum using std::reduce 72 | return std::reduce(std::execution::seq, partial_sums.begin(), 73 | partial_sums.end(), std::uint64_t{0}); 74 | } 75 | 76 | //----------------------------------------------------------------------------- 77 | /** 78 | * Unit tests for the compute_total_sum function. 79 | */ 80 | 81 | TEST(ComputeTotalSumTest, SingleThread) { 82 | constexpr std::size_t num_threads = 1; 83 | constexpr std::size_t total_elements = 10; 84 | 85 | // Expected sum: 0 + 1 + 2 + ... + 9 = 45 86 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 45); 87 | } 88 | 89 | TEST(ComputeTotalSumTest, MultipleThreadsEvenSplit) { 90 | constexpr std::size_t num_threads = 2; 91 | constexpr std::size_t total_elements = 10; 92 | 93 | // Expected sum: 0 + 1 + 2 + ... + 9 = 45 94 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 45); 95 | } 96 | 97 | TEST(ComputeTotalSumTest, MultipleThreadsUnevenSplit) { 98 | constexpr std::size_t num_threads = 3; 99 | constexpr std::size_t total_elements = 10; 100 | 101 | // Expected sum: 0 + 1 + 2 + ... + 9 = 45 102 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 45); 103 | } 104 | 105 | TEST(ComputeTotalSumTest, LargeNumberOfElements) { 106 | constexpr std::size_t num_threads = 4; 107 | constexpr std::size_t total_elements = 1'000'000; 108 | 109 | // Expected sum: Sum of integers from 0 to 999,999 110 | constexpr std::uint64_t expected_sum = 111 | (total_elements * (total_elements - 1)) / 2; 112 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), expected_sum); 113 | } 114 | 115 | TEST(ComputeTotalSumTest, ZeroElements) { 116 | constexpr std::size_t num_threads = 4; 117 | constexpr std::size_t total_elements = 0; 118 | 119 | // Expected sum: 0 120 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 0); 121 | } 122 | 123 | TEST(ComputeTotalSumTest, SingleElement) { 124 | constexpr std::size_t num_threads = 4; 125 | constexpr std::size_t total_elements = 1; 126 | 127 | // Expected sum: 0 128 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 0); 129 | } 130 | 131 | TEST(ComputeTotalSumTest, MoreThreadsThanElements) { 132 | constexpr std::size_t num_threads = 20; 133 | constexpr std::size_t total_elements = 10; 134 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 45); 135 | } 136 | 137 | TEST(ComputeTotalSumTest, ThreadsEqualToElements) { 138 | constexpr std::size_t num_threads = 10; 139 | constexpr std::size_t total_elements = 10; 140 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), 45); 141 | } 142 | 143 | TEST(ComputeTotalSumTest, ThousandsOfThreads) { 144 | constexpr std::size_t num_threads = 2000; 145 | constexpr std::size_t total_elements = 2000; 146 | 147 | // Sum of [0..1999] 148 | constexpr std::uint64_t expected_sum = 149 | (total_elements * (total_elements - 1)) / 2; 150 | 151 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), expected_sum); 152 | } 153 | 154 | TEST(ComputeTotalSumTest, MillionsOfElements) { 155 | constexpr std::size_t num_threads = 10; 156 | constexpr std::size_t total_elements = 2'000'000; 157 | 158 | // Sum of [0..1999] 159 | constexpr std::uint64_t expected_sum = 160 | (total_elements * (total_elements - 1)) / 2; 161 | 162 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), expected_sum); 163 | } 164 | 165 | TEST(ComputeTotalSumTest, HardwareConcurrencyThreads) { 166 | const auto num_threads = std::max(1u, std::thread::hardware_concurrency()); 167 | constexpr std::size_t total_elements = 1'000'000; 168 | 169 | const auto expected_sum = (total_elements * (total_elements - 1)) / 2; 170 | EXPECT_EQ(compute_total_sum(num_threads, total_elements), expected_sum); 171 | } 172 | 173 | TEST(ComputeTotalSumTest, ZeroThreadsShouldFail) { 174 | EXPECT_THROW(compute_total_sum(0, 10), std::invalid_argument); 175 | } -------------------------------------------------------------------------------- /src/main/jthread/lambda_jthread_main.cc: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * A demo for creating two threads 4 | * Run this using one of the following methods: 5 | * 1. With bazel: bazel run src/main:{THIS_FILE_NAME_WITHOUT_EXTENSION} 6 | * 2. With plain g++: g++ -std=c++17 -lpthread 7 | * src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * Calculates the sum of a range of numbers using multiple threads. 17 | * Each thread calculates the sum of a portion of the range. 18 | * The results are then combined to get the total sum. 19 | */ 20 | int main() { 21 | const uint64_t number_of_threads = 14; 22 | constexpr uint64_t number_of_elements = 1'000'000ULL; 23 | constexpr uint64_t step = number_of_elements / number_of_threads; 24 | 25 | std::vector partial_sums(number_of_threads, 0); 26 | { 27 | std::vector threads; 28 | for (uint64_t i = 0; i < number_of_threads; i++) { 29 | threads.emplace_back([i, &partial_sums]() { 30 | uint64_t local_sum = 0; 31 | uint64_t start = i * step; 32 | uint64_t end = 33 | (i == number_of_threads - 1) ? number_of_elements : (i + 1) * step; 34 | for (uint64_t j = start; j < end; j++) 35 | local_sum += j; 36 | partial_sums[i] = local_sum; 37 | }); 38 | } 39 | } 40 | 41 | for (uint64_t i = 0; i < number_of_threads; i++) 42 | std::cout << "partial_sums[" << i << "] = " << partial_sums[i] << std::endl; 43 | 44 | uint64_t total = 45 | std::accumulate(partial_sums.begin(), partial_sums.end(), uint64_t(0)); 46 | std::cout << "total: " << total << std::endl; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/jthread/pitfalls.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Don't forget to capture the stop_token in lambdas! 4 | void do_work(std::stop_token st) { 5 | // Do some work 6 | } 7 | 8 | // Don't forget to check the stop_token in loops! 9 | std::jthread t([](std::stop_token st) { 10 | while (true) 11 | do_work(); // ❌ ignores stop_token → cannot stop! 12 | }); 13 | 14 | std::jthread t([](std::stop_token st) { 15 | while (!st.stop_requested()) { // ✅ stop_token is checked 16 | do_work(); 17 | } 18 | }); 19 | 20 | // Don't forget to handle exceptions! 21 | // If an exception is thrown in the thread, it will terminate the program 22 | // unless handled properly. 23 | std::jthread t([](std::stop_token) { 24 | throw std::runtime_error("oops"); // ❌ terminate 25 | }); 26 | 27 | std::jthread t([](std::stop_token) { 28 | try { 29 | throw std::runtime_error("oops"); 30 | } catch (...) { 31 | // Handle the exception 32 | } 33 | }); 34 | 35 | // Don't forget to pass stop_token as the first argument! 36 | void doWorkWrong(int a, 37 | std::stop_token st) { // ❌ error: stop_token must be first 38 | while (!st.stop_requested()) { 39 | // Do work 40 | } 41 | } 42 | 43 | void doWorkRight(std::stop_token st, int a) { // ✅ stop_token is first 44 | while (!st.stop_requested()) { 45 | // Do work 46 | } 47 | } 48 | 49 | { 50 | std::lock_guard lock(m); // lock acquired 51 | // critical section 52 | } // lock released automatically here -------------------------------------------------------------------------------- /src/main/lambda_main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * A demo for creating two threads 3 | * Run this using one of the following methods: 4 | * 1. With bazel: bazel run src/main:{THIS_FILE_NAME_WITHOUT_EXTENSION} 5 | * 2. With plain g++: g++ -std=c++17 -lpthread 6 | * src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "src/lib/utility.h" 14 | 15 | /** 16 | * Calculates the sum of a range of numbers using multiple threads. 17 | * Each thread calculates the sum of a portion of the range. 18 | * The results are then combined to get the total sum. 19 | */ 20 | int main() 21 | { 22 | const int number_of_threads = 14; 23 | uint64_t number_of_elements = 1000 * 1000; 24 | // step is the number of elements each thread will process. 25 | // For example, if number_of_elements is 1000 and number_of_threads is 10, 26 | // then each thread will process 100 elements. 27 | uint64_t step = number_of_elements / number_of_threads; 28 | std::vector threads; 29 | std::vector partial_sums(number_of_threads); 30 | 31 | for (uint64_t i = 0; i < number_of_threads; i++) 32 | { 33 | // Creating a lambda function and adding it to the threads. 34 | threads.push_back(std::thread([i, &partial_sums, step] 35 | { 36 | // Iterate from i * step to (i + 1) * step and calculate the sum. 37 | for (uint64_t j = i * step; j < (i + 1) * step; j++) { 38 | partial_sums[i] += j; 39 | } })); 40 | } 41 | 42 | for (std::thread &t : threads) 43 | { 44 | if (t.joinable()) 45 | { 46 | t.join(); 47 | } 48 | } 49 | 50 | uint64_t total = 51 | std::accumulate(partial_sums.begin(), partial_sums.end(), uint64_t(0)); 52 | PrintVector(partial_sums); 53 | std::cout << "total: " << total << std::endl; 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /src/main/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "src/lib/utility.h" 7 | 8 | // A demo for creating two threads 9 | // Run this using one of the following methods: 10 | // 1. With bazel: bazel run src/main:main 11 | // 2. With plain g++: g++ -std=c++17 -lpthread src/main/main.cc -I ./ 12 | int main() { 13 | unsigned int n = std::thread::hardware_concurrency(); 14 | std::cout << n << " concurrent threads are supported.\n"; 15 | 16 | const int number_of_threads = 2; 17 | const int number_of_elements = 1000 * 1000 * 1000; 18 | const int step = number_of_elements / number_of_threads; 19 | std::vector partial_sums(number_of_threads); 20 | 21 | std::thread t1(AccumulateRange, std::ref(partial_sums[0]), 0, step); 22 | std::thread t2(AccumulateRange, std::ref(partial_sums[1]), step, 23 | number_of_threads * step); 24 | 25 | t1.join(); 26 | t2.join(); 27 | 28 | uint64_t total = 29 | std::accumulate(partial_sums.begin(), partial_sums.end(), uint64_t(0)); 30 | PrintVector(partial_sums); // Prints partial_sums 31 | std::cout << "total: " << total << std::endl; 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /src/main/map_reduce/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | 3 | cc_binary( 4 | name = "count_words", 5 | srcs = ["count_words.cc"], 6 | copts = [ 7 | "-std=c++20", 8 | ], 9 | deps = ["//src/main/map_reduce/lib:map_reduce"], 10 | ) 11 | 12 | 13 | cc_binary( 14 | name = "count_words_file", 15 | srcs = ["count_words_file.cc"], 16 | copts = [ 17 | "-std=c++20", 18 | ], 19 | deps = ["//src/main/map_reduce/lib:map_reduce"], 20 | ) 21 | -------------------------------------------------------------------------------- /src/main/map_reduce/count_words.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "lib/map_reduce.h" 15 | #include "lib/thread_safe_map.h" 16 | 17 | int main() { 18 | std::vector data = {"apple", "banana", "apple", 19 | "orange", "banana", "apple", 20 | "grape", "grape", "grape"}; 21 | 22 | MapReduce framework; 23 | 24 | auto results = framework.run( 25 | data, 26 | // Map function 27 | [](const std::string &item, 28 | std::map> &store) { 29 | store[item].push_back(1); 30 | }, 31 | // Reduce function 32 | [](const std::string &key, const std::vector &values) -> int { 33 | return std::accumulate(values.begin(), values.end(), 0); 34 | }, 35 | /*num_threads=*/4, // number of threads 36 | /*batch_size*/ 2 // batch size 37 | // Optional combiner can be provided here 38 | ); 39 | 40 | // Print results 41 | for (const auto &[key, sum] : results) { 42 | std::cout << key << ": " << sum << std::endl; 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/map_reduce/count_words_file.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "lib/map_reduce.h" 10 | 11 | // Utility: split a string into lowercase words 12 | std::vector split_words(const std::string &line) { 13 | std::istringstream iss(line); 14 | std::string word; 15 | std::vector words; 16 | 17 | while (iss >> word) { 18 | word.erase(std::remove_if(word.begin(), word.end(), 19 | [](char c) { return !std::isalnum(c); }), 20 | word.end()); 21 | std::transform(word.begin(), word.end(), word.begin(), ::tolower); 22 | if (!word.empty()) { 23 | words.push_back(word); 24 | } 25 | } 26 | return words; 27 | } 28 | 29 | int main(int argc, char *argv[]) { 30 | if (argc != 2) { 31 | std::cerr << "Usage: " << "count_words_file" << " \n"; 32 | return 1; 33 | } 34 | 35 | std::ifstream file(argv[1]); 36 | if (!file) { 37 | std::cerr << "Failed to open file: " << argv[1] << "\n"; 38 | return 1; 39 | } 40 | 41 | std::vector lines; 42 | std::string line; 43 | while (std::getline(file, line)) { 44 | lines.push_back(line); 45 | } 46 | 47 | MapReduce framework; 48 | 49 | auto result = framework.run( 50 | lines, 51 | // Map function: emit (word, 1) for each word in the line 52 | [](const std::string &line, 53 | std::map> &store) { 54 | for (const auto &word : split_words(line)) { 55 | store[word].push_back(1); 56 | } 57 | }, 58 | // Reduce function: sum all the 1s for each word 59 | [](const std::string &word, const std::vector &counts) { 60 | return std::accumulate(counts.begin(), counts.end(), 0); 61 | }); 62 | 63 | // Print top 20 words by count 64 | std::vector> sorted(result.begin(), result.end()); 65 | std::sort(sorted.begin(), sorted.end(), 66 | [](const auto &a, const auto &b) { return b.second < a.second; }); 67 | 68 | for (size_t i = 0; i < std::min(200, sorted.size()); ++i) { 69 | std::cout << sorted[i].first << ": " << sorted[i].second << '\n'; 70 | } 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /src/main/map_reduce/khayam.txt: -------------------------------------------------------------------------------- 1 | WAKE! For the Sun, who scatter'd into flight 2 | The Stars before him from the Field of Night, 3 | Drives Night along with them from Heav'n, and strikes 4 | The Sultan's Turret with a Shaft of Light. 5 | II. 6 | 7 | Before the phantom of False morning died, 8 | Methought a Voice within the Tavern cried, 9 | "When all the Temple is prepared within, 10 | "Why nods the drowsy Worshiper outside?" 11 | III. 12 | 13 | And, as the Cock crew, those who stood before 14 | The Tavern shouted—"Open then the Door! 15 | "You know how little while we have to stay, 16 | And, once departed, may return no more." 17 | IV. 18 | 19 | Now the New Year reviving old Desires, 20 | The thoughtful Soul to Solitude retires, 21 | Where the WHITE HAND OF MOSES on the Bough 22 | Puts out, and Jesus from the Ground suspires. 23 | V. 24 | 25 | Iram indeed is gone with all his Rose, 26 | And Jamshyd's Sev'n-ring'd Cup where no one knows; 27 | But still a Ruby kindles in the Vine, 28 | And many a Garden by the Water blows. 29 | VI. 30 | 31 | And David's lips are lockt; but in divine 32 | High-piping Pehlevi, with "Wine! Wine! Wine! 33 | "Red Wine!"—the Nightingale cries to the Rose 34 | That sallow cheek of hers to' incarnadine. 35 | VII. 36 | 37 | Come, fill the Cup, and in the fire of Spring 38 | Your Winter garment of Repentance fling: 39 | The Bird of Time has but a little way 40 | To flutter—and the Bird is on the Wing. 41 | VIII. 42 | 43 | Whether at Naishapur or Babylon, 44 | Whether the Cup with sweet or bitter run, 45 | The Wine of Life keeps oozing drop by drop, 46 | The Leaves of Life keep falling one by one. 47 | IX. 48 | 49 | Each Morn a thousand Roses brings, you say: 50 | Yes, but where leaves the Rose of Yesterday? 51 | And this first Summer month that brings the Rose 52 | Shall take Jamshyd and Kaikobad away. 53 | X. 54 | 55 | Well, let it take them! What have we to do 56 | With Kaikobad the Great, or Kaikhosru? 57 | Let Zal and Rustum bluster as they will, 58 | Or Hatim call to Supper—heed not you. 59 | XI. 60 | 61 | With me along the strip of Herbage strown 62 | That just divides the desert from the sown, 63 | Where name of Slave and Sultan is forgot— 64 | And Peace to Mahmud on his golden Throne! 65 | XII. 66 | 67 | A Book of Verses underneath the Bough, 68 | A Jug of Wine, a Loaf of Bread—and Thou 69 | Beside me singing in the Wilderness— 70 | Oh, Wilderness were Paradise enow! 71 | XIII. 72 | 73 | Some for the Glories of This World; and some 74 | Sigh for the Prophet's Paradise to come; 75 | Ah, take the Cash, and let the Credit go, 76 | Nor heed the rumble of a distant Drum! 77 | XIV. 78 | 79 | Look to the blowing Rose about us—"Lo, 80 | Laughing," she says, "into the world I blow, 81 | At once the silken tassel of my Purse 82 | Tear, and its Treasure on the Garden throw." 83 | XV. 84 | 85 | And those who husbanded the Golden grain, 86 | And those who flung it to the winds like Rain, 87 | Alike to no such aureate Earth are turn'd 88 | As, buried once, Men want dug up again. 89 | XVI. 90 | 91 | The Worldly Hope men set their Hearts upon 92 | Turns Ashes—or it prospers; and anon, 93 | Like Snow upon the Desert's dusty Face, 94 | Lighting a little hour or two—is gone. 95 | XVII. 96 | 97 | Think, in this batter'd Caravanserai 98 | Whose Portals are alternate Night and Day, 99 | How Sultan after Sultan with his Pomp 100 | Abode his destined Hour, and went his way. 101 | XVIII. 102 | 103 | They say the Lion and the Lizard keep 104 | The courts where Jamshyd gloried and drank deep: 105 | And Bahram, that great Hunter—the Wild Ass 106 | Stamps o'er his Head, but cannot break his Sleep. 107 | XIX. 108 | 109 | I sometimes think that never blows so red 110 | The Rose as where some buried Caesar bled; 111 | That every Hyacinth the Garden wears 112 | Dropt in her Lap from some once lovely Head. 113 | XX. 114 | 115 | And this reviving Herb whose tender Green 116 | Fledges the River-Lip on which we lean— 117 | Ah, lean upon it lightly! for who knows 118 | From what once lovely Lip it springs unseen! 119 | XXI. 120 | 121 | Ah, my Beloved, fill the Cup that clears 122 | TO-DAY of past Regrets and future Fears: 123 | To-morrow—Why, To-morrow I may be 124 | Myself with Yesterday's Sev'n thousand Years. 125 | XXII. 126 | 127 | For some we loved, the loveliest and the best 128 | That from his Vintage rolling Time hath prest, 129 | Have drunk their Cup a Round or two before, 130 | And one by one crept silently to rest. 131 | XXIII. 132 | 133 | And we, that now make merry in the Room 134 | They left, and Summer dresses in new bloom, 135 | Ourselves must we beneath the Couch of Earth 136 | Descend—ourselves to make a Couch—for whom? 137 | XXIV. 138 | 139 | Ah, make the most of what we yet may spend, 140 | Before we too into the Dust descend; 141 | Dust into Dust, and under Dust to lie, 142 | Sans Wine, sans Song, sans Singer, and—sans End! 143 | XXV. 144 | 145 | Alike for those who for TO-DAY prepare, 146 | And those that after some TO-MORROW stare, 147 | A Muezzin from the Tower of Darkness cries, 148 | "Fools! your Reward is neither Here nor There." 149 | XXVI. 150 | 151 | Why, all the Saints and Sages who discuss'd 152 | Of the Two Worlds so wisely—they are thrust 153 | Like foolish Prophets forth; their Words to Scorn 154 | Are scatter'd, and their Mouths are stopt with Dust. 155 | XXVII. 156 | 157 | Myself when young did eagerly frequent 158 | Doctor and Saint, and heard great argument 159 | About it and about: but evermore 160 | Came out by the same door where in I went. 161 | XXVIII. 162 | 163 | With them the seed of Wisdom did I sow, 164 | And with mine own hand wrought to make it grow; 165 | And this was all the Harvest that I reap'd— 166 | "I came like Water, and like Wind I go." 167 | XXIX. 168 | 169 | Into this Universe, and Why not knowing 170 | Nor Whence, like Water willy-nilly flowing; 171 | And out of it, as Wind along the Waste, 172 | I know not Whither, willy-nilly blowing. 173 | XXX. 174 | 175 | What, without asking, hither hurried Whence? 176 | And, without asking, Whither hurried hence! 177 | Oh, many a Cup of this forbidden Wine 178 | Must drown the memory of that insolence! 179 | XXXI. 180 | 181 | Up from Earth's Center through the Seventh Gate 182 | I rose, and on the Throne of Saturn sate, 183 | And many a Knot unravel'd by the Road; 184 | But not the Master-knot of Human Fate. 185 | XXXII. 186 | 187 | There was the Door to which I found no Key; 188 | There was the Veil through which I might not see: 189 | Some little talk awhile of ME and THEE 190 | There was—and then no more of THEE and ME. 191 | XXXIII. 192 | 193 | Earth could not answer; nor the Seas that mourn 194 | In flowing Purple, of their Lord Forlorn; 195 | Nor rolling Heaven, with all his Signs reveal'd 196 | And hidden by the sleeve of Night and Morn. 197 | XXXIV. 198 | 199 | Then of the THEE IN ME who works behind 200 | The Veil, I lifted up my hands to find 201 | A lamp amid the Darkness; and I heard, 202 | As from Without—"THE ME WITHIN THEE BLIND!" 203 | XXXV. 204 | 205 | Then to the Lip of this poor earthen Urn 206 | I lean'd, the Secret of my Life to learn: 207 | And Lip to Lip it murmur'd—"While you live, 208 | "Drink!—for, once dead, you never shall return." 209 | XXXVI. 210 | 211 | I think the Vessel, that with fugitive 212 | Articulation answer'd, once did live, 213 | And drink; and Ah! the passive Lip I kiss'd, 214 | How many Kisses might it take—and give! 215 | XXXVII. 216 | 217 | For I remember stopping by the way 218 | To watch a Potter thumping his wet Clay: 219 | And with its all-obliterated Tongue 220 | It murmur'd—"Gently, Brother, gently, pray!" 221 | XXXVIII. 222 | 223 | And has not such a Story from of Old 224 | Down Man's successive generations roll'd 225 | Of such a clod of saturated Earth 226 | Cast by the Maker into Human mold? 227 | XXXIX. 228 | 229 | And not a drop that from our Cups we throw 230 | For Earth to drink of, but may steal below 231 | To quench the fire of Anguish in some Eye 232 | There hidden—far beneath, and long ago. 233 | XL. 234 | 235 | As then the Tulip for her morning sup 236 | Of Heav'nly Vintage from the soil looks up, 237 | Do you devoutly do the like, till Heav'n 238 | To Earth invert you—like an empty Cup. 239 | XLI. 240 | 241 | Perplext no more with Human or Divine, 242 | To-morrow's tangle to the winds resign, 243 | And lose your fingers in the tresses of 244 | The Cypress-slender Minister of Wine. 245 | XLII. 246 | 247 | And if the Wine you drink, the Lip you press, 248 | End in what All begins and ends in—Yes; 249 | Think then you are TO-DAY what YESTERDAY 250 | You were—TO-MORROW you shall not be less. 251 | XLIII. 252 | 253 | So when that Angel of the darker Drink 254 | At last shall find you by the river-brink, 255 | And, offering his Cup, invite your Soul 256 | Forth to your Lips to quaff—you shall not shrink. 257 | XLIV. 258 | 259 | Why, if the Soul can fling the Dust aside, 260 | And naked on the Air of Heaven ride, 261 | Were't not a Shame—were't not a Shame for him 262 | In this clay carcass crippled to abide? 263 | XLV. 264 | 265 | 'Tis but a Tent where takes his one day's rest 266 | A Sultan to the realm of Death addrest; 267 | The Sultan rises, and the dark Ferrash 268 | Strikes, and prepares it for another Guest. 269 | XLVI. 270 | 271 | And fear not lest Existence closing your 272 | Account, and mine, should know the like no more; 273 | The Eternal Saki from that Bowl has pour'd 274 | Millions of Bubbles like us, and will pour. 275 | XLVII. 276 | 277 | When You and I behind the Veil are past, 278 | Oh, but the long, long while the World shall last, 279 | Which of our Coming and Departure heeds 280 | As the Sea's self should heed a pebble-cast. 281 | XLVIII. 282 | 283 | A Moment's Halt—a momentary taste 284 | Of BEING from the Well amid the Waste— 285 | And Lo!—the phantom Caravan has reach'd 286 | The NOTHING it set out from—Oh, make haste! 287 | XLIX. 288 | 289 | Would you that spangle of Existence spend 290 | About THE SECRET—quick about it, Friend! 291 | A Hair perhaps divides the False from True— 292 | And upon what, prithee, may life depend? 293 | L. 294 | 295 | A Hair perhaps divides the False and True; 296 | Yes; and a single Alif were the clue— 297 | Could you but find it—to the Treasure-house, 298 | And peradventure to THE MASTER too; 299 | LI. 300 | 301 | Whose secret Presence through Creation's veins 302 | Running Quicksilver-like eludes your pains; 303 | Taking all shapes from Mah to Mahi and 304 | They change and perish all—but He remains; 305 | LII. 306 | 307 | A moment guessed—then back behind the Fold 308 | Immerst of Darkness round the Drama roll'd 309 | Which, for the Pastime of Eternity, 310 | He doth Himself contrive, enact, behold. 311 | LIII. 312 | 313 | But if in vain, down on the stubborn floor 314 | Of Earth, and up to Heav'n's unopening Door, 315 | You gaze TO-DAY, while You are You—how then 316 | TO-MORROW, when You shall be You no more? 317 | LIV. 318 | 319 | Waste not your Hour, nor in the vain pursuit 320 | Of This and That endeavor and dispute; 321 | Better be jocund with the fruitful Grape 322 | Than sadden after none, or bitter, Fruit. 323 | LV. 324 | 325 | You know, my Friends, with what a brave Carouse 326 | I made a Second Marriage in my house; 327 | Divorced old barren Reason from my Bed, 328 | And took the Daughter of the Vine to Spouse. 329 | LVI. 330 | 331 | For "Is" and "Is-not" though with Rule and Line 332 | And "UP-AND-DOWN" by Logic I define, 333 | Of all that one should care to fathom, I 334 | was never deep in anything but—Wine. 335 | LVII. 336 | 337 | Ah, by my Computations, People say, 338 | Reduce the Year to better reckoning?—Nay, 339 | 'Twas only striking from the Calendar 340 | Unborn To-morrow and dead Yesterday. 341 | LVIII. 342 | 343 | And lately, by the Tavern Door agape, 344 | Came shining through the Dusk an Angel Shape 345 | Bearing a Vessel on his Shoulder; and 346 | He bid me taste of it; and 'twas—the Grape! 347 | LIX. 348 | 349 | The Grape that can with Logic absolute 350 | The Two-and-Seventy jarring Sects confute: 351 | The sovereign Alchemist that in a trice 352 | Life's leaden metal into Gold transmute; 353 | LX. 354 | 355 | The mighty Mahmud, Allah-breathing Lord, 356 | That all the misbelieving and black Horde 357 | Of Fears and Sorrows that infest the Soul 358 | Scatters before him with his whirlwind Sword. 359 | LXI. 360 | 361 | Why, be this Juice the growth of God, who dare 362 | Blaspheme the twisted tendril as a Snare? 363 | A Blessing, we should use it, should we not? 364 | And if a Curse—why, then, Who set it there? 365 | LXII. 366 | 367 | I must abjure the Balm of Life, I must, 368 | Scared by some After-reckoning ta'en on trust, 369 | Or lured with Hope of some Diviner Drink, 370 | To fill the Cup—when crumbled into Dust! 371 | LXIII. 372 | 373 | Of threats of Hell and Hopes of Paradise! 374 | One thing at least is certain—This Life flies; 375 | One thing is certain and the rest is Lies; 376 | The Flower that once has blown for ever dies. 377 | LXIV. 378 | 379 | Strange, is it not? that of the myriads who 380 | Before us pass'd the door of Darkness through, 381 | Not one returns to tell us of the Road, 382 | Which to discover we must travel too. 383 | LXV. 384 | 385 | The Revelations of Devout and Learn'd 386 | Who rose before us, and as Prophets burn'd, 387 | Are all but Stories, which, awoke from Sleep 388 | They told their comrades, and to Sleep return'd. 389 | LXVI. 390 | 391 | I sent my Soul through the Invisible, 392 | Some letter of that After-life to spell: 393 | And by and by my Soul return'd to me, 394 | And answer'd "I Myself am Heav'n and Hell:" 395 | LXVII. 396 | 397 | Heav'n but the Vision of fulfill'd Desire, 398 | And Hell the Shadow from a Soul on fire, 399 | Cast on the Darkness into which Ourselves, 400 | So late emerged from, shall so soon expire. 401 | LXVIII. 402 | 403 | We are no other than a moving row 404 | Of Magic Shadow-shapes that come and go 405 | Round with the Sun-illumined Lantern held 406 | In Midnight by the Master of the Show; 407 | LXIX. 408 | 409 | But helpless Pieces of the Game He plays 410 | Upon this Chequer-board of Nights and Days; 411 | Hither and thither moves, and checks, and slays, 412 | And one by one back in the Closet lays. 413 | LXX. 414 | 415 | The Ball no question makes of Ayes and Noes, 416 | But Here or There as strikes the Player goes; 417 | And He that toss'd you down into the Field, 418 | He knows about it all—HE knows—HE knows! 419 | LXXI. 420 | 421 | The Moving Finger writes; and, having writ, 422 | Moves on: nor all your Piety nor Wit 423 | Shall lure it back to cancel half a Line, 424 | Nor all your Tears wash out a Word of it. 425 | LXXII. 426 | 427 | And that inverted Bowl they call the Sky, 428 | Whereunder crawling coop'd we live and die, 429 | Lift not your hands to It for help—for It 430 | As impotently moves as you or I. 431 | LXXIII. 432 | 433 | With Earth's first Clay They did the Last Man knead, 434 | And there of the Last Harvest sow'd the Seed: 435 | And the first Morning of Creation wrote 436 | What the Last Dawn of Reckoning shall read. 437 | LXXIV. 438 | 439 | YESTERDAY This Day's Madness did prepare; 440 | TO-MORROW's Silence, Triumph, or Despair: 441 | Drink! for you not know whence you came, nor why: 442 | Drink! for you know not why you go, nor where. 443 | LXXV. 444 | 445 | I tell you this—When, started from the Goal, 446 | Over the flaming shoulders of the Foal 447 | Of Heav'n Parwin and Mushtari they flung, 448 | In my predestined Plot of Dust and Soul. 449 | LXXVI. 450 | 451 | The Vine had struck a fiber: which about 452 | It clings my Being—let the Dervish flout; 453 | Of my Base metal may be filed a Key 454 | That shall unlock the Door he howls without. 455 | LXXVII. 456 | 457 | And this I know: whether the one True Light 458 | Kindle to Love, or Wrath consume me quite, 459 | One Flash of It within the Tavern caught 460 | Better than in the Temple lost outright. 461 | LXXVIII. 462 | 463 | What! out of senseless Nothing to provoke 464 | A conscious Something to resent the yoke 465 | Of unpermitted Pleasure, under pain 466 | Of Everlasting Penalties, if broke! 467 | LXXIX. 468 | 469 | What! from his helpless Creature be repaid 470 | Pure Gold for what he lent him dross-allay'd— 471 | Sue for a Debt he never did contract, 472 | And cannot answer—Oh the sorry trade! 473 | LXXX. 474 | 475 | Oh Thou, who didst with pitfall and with gin 476 | Beset the Road I was to wander in, 477 | Thou wilt not with Predestined Evil round 478 | Enmesh, and then impute my Fall to Sin! 479 | LXXXI. 480 | 481 | Oh Thou, who Man of baser Earth didst make, 482 | And ev'n with Paradise devise the Snake: 483 | For all the Sin wherewith the Face of Man 484 | Is blacken'd—Man's forgiveness give—and take! 485 | LXXXII. 486 | 487 | As under cover of departing Day 488 | Slunk hunger-stricken Ramazan away, 489 | Once more within the Potter's house alone 490 | I stood, surrounded by the Shapes of Clay. 491 | LXXXIII. 492 | 493 | Shapes of all Sorts and Sizes, great and small, 494 | That stood along the floor and by the wall; 495 | And some loquacious Vessels were; and some 496 | Listen'd perhaps, but never talk'd at all. 497 | LXXXIV. 498 | 499 | Said one among them—"Surely not in vain 500 | My substance of the common Earth was ta'en 501 | And to this Figure molded, to be broke, 502 | Or trampled back to shapeless Earth again." 503 | LXXXV. 504 | 505 | Then said a Second—"Ne'er a peevish Boy 506 | Would break the Bowl from which he drank in joy; 507 | And He that with his hand the Vessel made 508 | Will surely not in after Wrath destroy." 509 | LXXXVI. 510 | 511 | After a momentary silence spake 512 | Some Vessel of a more ungainly Make; 513 | "They sneer at me for leaning all awry: 514 | What! did the Hand then of the Potter shake?" 515 | LXXXVII. 516 | 517 | Whereat some one of the loquacious Lot— 518 | I think a Sufi pipkin—waxing hot— 519 | "All this of Pot and Potter—Tell me then, 520 | Who is the Potter, pray, and who the Pot?" 521 | LXXXVIII. 522 | 523 | "Why," said another, "Some there are who tell 524 | Of one who threatens he will toss to Hell 525 | The luckless Pots he marr'd in making—Pish! 526 | He's a Good Fellow, and 'twill all be well." 527 | LXXXIX. 528 | 529 | "Well," murmured one, "Let whoso make or buy, 530 | My Clay with long Oblivion is gone dry: 531 | But fill me with the old familiar Juice, 532 | Methinks I might recover by and by." 533 | XC. 534 | 535 | So while the Vessels one by one were speaking, 536 | The little Moon look'd in that all were seeking: 537 | And then they jogg'd each other, "Brother! Brother! 538 | Now for the Porter's shoulders' knot a-creaking!" 539 | XCI. 540 | 541 | Ah, with the Grape my fading life provide, 542 | And wash the Body whence the Life has died, 543 | And lay me, shrouded in the living Leaf, 544 | By some not unfrequented Garden-side. 545 | XCII. 546 | 547 | That ev'n buried Ashes such a snare 548 | Of Vintage shall fling up into the Air 549 | As not a True-believer passing by 550 | But shall be overtaken unaware. 551 | XCIII. 552 | 553 | Indeed the Idols I have loved so long 554 | Have done my credit in this World much wrong: 555 | Have drown'd my Glory in a shallow Cup, 556 | And sold my reputation for a Song. 557 | XCIV. 558 | 559 | Indeed, indeed, Repentance oft before 560 | I swore—but was I sober when I swore? 561 | And then and then came Spring, and Rose-in-hand 562 | My thread-bare Penitence apieces tore. 563 | XCV. 564 | 565 | And much as Wine has play'd the Infidel, 566 | And robb'd me of my Robe of Honor—Well, 567 | I wonder often what the Vintners buy 568 | One half so precious as the stuff they sell. 569 | XCVI. 570 | 571 | Yet Ah, that Spring should vanish with the Rose! 572 | That Youth's sweet-scented manuscript should close! 573 | The Nightingale that in the branches sang, 574 | Ah whence, and whither flown again, who knows! 575 | XCVII. 576 | 577 | Would but the Desert of the Fountain yield 578 | One glimpse—if dimly, yet indeed, reveal'd, 579 | To which the fainting Traveler might spring, 580 | As springs the trampled herbage of the field! 581 | XCVIII. 582 | 583 | Would but some winged Angel ere too late 584 | Arrest the yet unfolded Roll of Fate, 585 | And make the stern Recorder otherwise 586 | Enregister, or quite obliterate! 587 | XCIX. 588 | 589 | Ah Love! could you and I with Him conspire 590 | To grasp this sorry Scheme of Things entire, 591 | Would not we shatter it to bits—and then 592 | Re-mold it nearer to the Heart's Desire! 593 | C. 594 | 595 | Yon rising Moon that looks for us again— 596 | How oft hereafter will she wax and wane; 597 | How oft hereafter rising look for us 598 | Through this same Garden—and for one in vain! 599 | CI. 600 | 601 | And when like her, oh Saki, you shall pass 602 | Among the Guests Star-scatter'd on the Grass, 603 | And in your joyous errand reach the spot 604 | Where I made One—turn down an empty Glass! 605 | 606 | -------------------------------------------------------------------------------- /src/main/map_reduce/lib/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | 3 | cc_library( 4 | name = "map_reduce", 5 | hdrs = [ 6 | "map_reduce.h", 7 | "thread_safe_map.h", 8 | ], 9 | copts = [ 10 | "-std=c++23", 11 | ], 12 | includes = ["."], # optional: makes local includes work 13 | visibility = ["//visibility:public"], 14 | ) 15 | -------------------------------------------------------------------------------- /src/main/map_reduce/lib/map_reduce.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "thread_safe_map.h" 15 | 16 | // Generic MapReduce framework supporting customizable map, combine, reduce 17 | // Supports multithreaded map and reduce stages with optional combiner phase 18 | template 19 | class MapReduce { 20 | public: 21 | // MapFunction: 22 | // param 1: const string&: The raw input record (e.g., a line from a file) 23 | // param 2: map>&: Per-thread local store to emit 24 | // intermediate key-value pairs 25 | using MapFunction = std::function> &)>; 27 | 28 | // CombineFunction: 29 | // param 1: vector of per-thread maps (input from all map threads) 30 | // param 2: shared thread-safe global store to merge into 31 | using CombineFunction = 32 | std::function>> &, 33 | ThreadSafeMap &)>; 34 | 35 | // ReduceFunction: 36 | // param 1: const Key&: The key to reduce 37 | // param 2: const vector&: All values associated with the key 38 | // returns: ResultType - the reduced result for the key 39 | template 40 | using ReduceFunction = 41 | std::function &)>; 42 | 43 | // Executes the MapReduce job over the given input strings 44 | // Supports optional custom combiner and batch size 45 | template 46 | std::map run( 47 | const std::vector &inputs, MapFunction map_func, 48 | ReduceFunction reduce_func, 49 | size_t num_threads = std::thread::hardware_concurrency(), 50 | size_t batch_size = 1, CombineFunction combiner = nullptr) { 51 | ThreadSafeMap store; 52 | std::atomic map_progress(0); 53 | std::atomic reduce_progress(0); 54 | 55 | size_t total = inputs.size(); 56 | size_t chunk_size = std::ceil((double)total / num_threads); 57 | 58 | // Each thread gets its own local map to avoid contention 59 | std::vector>> localMaps(num_threads); 60 | 61 | // Map phase 62 | { 63 | std::vector mappers; 64 | for (size_t t = 0; t < num_threads; t++) { 65 | size_t start = t * chunk_size; 66 | size_t end = std::min(start + chunk_size, total); 67 | if (start >= end) { 68 | break; 69 | } 70 | size_t s = start, e = end, tid = t; 71 | 72 | // Each thread maps its chunk into its own local map 73 | mappers.emplace_back([&, s, e, tid]() { 74 | for (size_t i = s; i < e; i += batch_size) { 75 | size_t batch_end = std::min(i + batch_size, e); 76 | for (size_t j = i; j < batch_end; j++) { 77 | map_func(inputs[j], localMaps[tid]); 78 | } 79 | map_progress += batch_end - i; 80 | } 81 | }); 82 | } 83 | } // jthreads auto-join on scope exit 84 | 85 | // Optional combiner: merges local maps into global store 86 | if (combiner) { 87 | combiner(localMaps, store); 88 | } else { 89 | // Default combine: simple merge with locking 90 | for (const auto &local : localMaps) { 91 | for (const auto &[key, values] : local) { 92 | for (const auto &v : values) { 93 | store.add(key, v); 94 | } 95 | } 96 | } 97 | } 98 | 99 | // Reduce phase: partition keys and reduce in parallel 100 | auto data = store.get_data(); 101 | std::vector>> items(data.begin(), 102 | data.end()); 103 | size_t reduce_chunk = std::ceil((double)items.size() / num_threads); 104 | 105 | std::map results; 106 | std::mutex results_mtx; 107 | 108 | { 109 | std::vector reducers; 110 | for (size_t t = 0; t < num_threads; t++) { 111 | size_t start = t * reduce_chunk; 112 | size_t end = std::min(start + reduce_chunk, items.size()); 113 | if (start >= end) { 114 | break; 115 | } 116 | 117 | // Each thread reduces a slice of key-value groups 118 | reducers.emplace_back([&, start, end]() { 119 | for (size_t i = start; i < end; i++) { 120 | ResultType result = reduce_func(items[i].first, items[i].second); 121 | { 122 | std::lock_guard lock(results_mtx); 123 | results[items[i].first] = result; 124 | } 125 | reduce_progress++; 126 | } 127 | }); 128 | } 129 | } // jthreads auto-join on scope exit 130 | 131 | return results; 132 | } 133 | }; 134 | -------------------------------------------------------------------------------- /src/main/map_reduce/lib/thread_safe_map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | class ThreadSafeMap { 9 | std::map> store; 10 | std::mutex mtx; 11 | 12 | public: 13 | void add(const Key &key, const Value &value) { 14 | std::lock_guard lock(mtx); 15 | store[key].push_back(value); 16 | } 17 | 18 | std::map> get_data() { 19 | std::lock_guard lock(mtx); 20 | return store; 21 | } 22 | }; -------------------------------------------------------------------------------- /src/main/map_reduce/tests/BUILD: -------------------------------------------------------------------------------- 1 | cc_test( 2 | name = "map_reduce_test", 3 | srcs = ["map_reduce_test.cc"], 4 | copts = [ 5 | "-std=c++23", 6 | ], 7 | deps = [ 8 | "//src/main/map_reduce/lib:map_reduce", 9 | "@googletest//:gtest_main", 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /src/main/map_reduce/tests/map_reduce_test.cc: -------------------------------------------------------------------------------- 1 | #include "../lib/map_reduce.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | TEST(MapReduceTest, SimpleWordCount) { 12 | std::vector data = {"apple", "banana", "apple", 13 | "orange", "banana", "apple", 14 | "grape", "grape", "grape"}; 15 | 16 | MapReduce framework; 17 | 18 | auto results = framework.run( 19 | data, 20 | // Map function 21 | [](const std::string &item, 22 | std::map> &store) { 23 | store[item].push_back(1); 24 | }, 25 | // Reduce function 26 | [](const std::string &key, const std::vector &values) -> int { 27 | return std::accumulate(values.begin(), values.end(), 0); 28 | }, 29 | 2, 2); 30 | 31 | EXPECT_EQ(results["apple"], 3); 32 | EXPECT_EQ(results["banana"], 2); 33 | EXPECT_EQ(results["orange"], 1); 34 | EXPECT_EQ(results["grape"], 3); 35 | } 36 | 37 | TEST(MapReduceTest, EmptyInput) { 38 | std::vector data; 39 | MapReduce framework; 40 | 41 | auto results = framework.run( 42 | data, 43 | [](const std::string &item, 44 | std::map> &store) { 45 | store[item].push_back(1); 46 | }, 47 | [](const std::string &key, const std::vector &values) -> int { 48 | return std::accumulate(values.begin(), values.end(), 0); 49 | }); 50 | 51 | EXPECT_TRUE(results.empty()); 52 | } 53 | 54 | TEST(MapReduceTest, SingleElement) { 55 | std::vector data = {"kiwi"}; 56 | MapReduce framework; 57 | 58 | auto results = framework.run( 59 | data, 60 | [](const std::string &item, 61 | std::map> &store) { 62 | store[item].push_back(1); 63 | }, 64 | [](const std::string &key, const std::vector &values) -> int { 65 | return std::accumulate(values.begin(), values.end(), 0); 66 | }); 67 | 68 | ASSERT_EQ(results.size(), 1); 69 | EXPECT_EQ(results["kiwi"], 1); 70 | } 71 | 72 | TEST(MapReduceTest, CaseInsensitiveWordCount) { 73 | std::vector data = {"Apple", "apple", "APPLE", "Banana", 74 | "BANANA"}; 75 | MapReduce framework; 76 | 77 | auto results = framework.run( 78 | data, 79 | [](const std::string &item, 80 | std::map> &store) { 81 | std::string lower = item; 82 | std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); 83 | store[lower].push_back(1); 84 | }, 85 | [](const std::string &key, const std::vector &values) -> int { 86 | return std::accumulate(values.begin(), values.end(), 0); 87 | }); 88 | 89 | EXPECT_EQ(results["apple"], 3); 90 | EXPECT_EQ(results["banana"], 2); 91 | } 92 | 93 | TEST(MapReduceTest, CustomReduceSumLengths) { 94 | std::vector data = {"a", "bb", "ccc", "a", "bb"}; 95 | MapReduce framework; 96 | 97 | auto results = framework.run( 98 | data, 99 | [](const std::string &item, 100 | std::map> &store) { 101 | store[item].push_back(item.length()); 102 | }, 103 | [](const std::string &key, const std::vector &values) -> int { 104 | // Sum the lengths for each key 105 | return std::accumulate(values.begin(), values.end(), 0); 106 | }); 107 | 108 | EXPECT_EQ(results["a"], 2); // 1 + 1 109 | EXPECT_EQ(results["bb"], 4); // 2 + 2 110 | EXPECT_EQ(results["ccc"], 3); // 3 111 | } 112 | 113 | TEST(MapReduceTest, LargeInput) { 114 | std::vector data; 115 | for (int i = 0; i < 10000; ++i) { 116 | data.push_back("x"); 117 | } 118 | MapReduce framework; 119 | 120 | auto results = framework.run( 121 | data, 122 | [](const std::string &item, 123 | std::map> &store) { 124 | store[item].push_back(1); 125 | }, 126 | [](const std::string &key, const std::vector &values) -> int { 127 | return std::accumulate(values.begin(), values.end(), 0); 128 | }, 129 | 4, 100); 130 | 131 | EXPECT_EQ(results["x"], 10000); 132 | } 133 | 134 | TEST(MapReduceTest, MultipleKeysAndValues) { 135 | std::vector data = {"a", "b", "c", "a", "b", "a"}; 136 | MapReduce framework; 137 | 138 | auto results = framework.run( 139 | data, 140 | [](const std::string &item, 141 | std::map> &store) { 142 | store[item].push_back(2); 143 | store[item].push_back(3); 144 | }, 145 | [](const std::string &key, const std::vector &values) -> int { 146 | return std::accumulate(values.begin(), values.end(), 0); 147 | }); 148 | 149 | EXPECT_EQ(results["a"], 15); // (2+3) * 3 150 | EXPECT_EQ(results["b"], 10); // (2+3) * 2 151 | EXPECT_EQ(results["c"], 5); // (2+3) * 1 152 | } -------------------------------------------------------------------------------- /src/main/memory_order/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_binary( 3 | name = "main", 4 | srcs = ["main.cc"], 5 | deps = ["//src/lib:SolutionLib"], 6 | ) 7 | 8 | cc_binary( 9 | name = "memory_order_main", 10 | srcs = ["memory_order_main.cc"], 11 | deps = ["//src/lib:SolutionLib"], 12 | ) 13 | 14 | 15 | cc_binary( 16 | name = "memory_order_counter_main", 17 | srcs = ["memory_order_counter_main.cc"], 18 | deps = ["//src/lib:SolutionLib"], 19 | ) 20 | 21 | cc_binary( 22 | name = "release_acquire_main", 23 | srcs = ["release_acquire_main.cc"], 24 | deps = ["//src/lib:SolutionLib"], 25 | ) 26 | 27 | cc_binary( 28 | name = "release_acquire_3t_main", 29 | srcs = ["release_acquire_3t_main.cc"], 30 | deps = ["//src/lib:SolutionLib"], 31 | ) 32 | 33 | 34 | cc_binary( 35 | name = "release_acquire_main_simple", 36 | srcs = ["release_acquire_main_simple.cc"], 37 | deps = ["//src/lib:SolutionLib"], 38 | ) 39 | 40 | cc_binary( 41 | name = "seq_cst_main", 42 | srcs = ["seq_cst_main.cc"], 43 | deps = [], 44 | ) 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/memory_order/aquire_release_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Global variables 8 | std::atomic flag(0); 9 | int b = 0; 10 | int x = 0, y = 0; 11 | std::string msg, text; 12 | 13 | void f1() { 14 | msg = "Hello world"; 15 | flag.store(1, std::memory_order_release); 16 | b += flag; 17 | } 18 | 19 | void f2() { 20 | text = "Hi"; 21 | while (flag.load(std::memory_order_acquire) != 1) 22 | ; 23 | text = msg; 24 | } 25 | 26 | int main() { 27 | std::vector threads; 28 | 29 | threads.push_back(std::thread(f1)); 30 | threads.push_back(std::thread(f2)); 31 | 32 | for (std::thread &t : threads) { 33 | if (t.joinable()) { 34 | t.join(); 35 | } 36 | } 37 | 38 | std::cout << "msg: " << msg << std::endl; 39 | std::cout << "b: " << b << ", y: " << y << std::endl; 40 | std::cout << "text: " << text << std::endl; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/memory_order/memory_order_counter_main copy 2.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Demo Relaxed ordering 7 | 8 | std::atomic cnt = {0}; 9 | 10 | void f() { 11 | for (int n = 0; n < 1000; ++n) { 12 | cnt.fetch_add(1, std::memory_order_relaxed); 13 | } 14 | } 15 | 16 | int main() { 17 | std::vector v; 18 | for (int n = 0; n < 10; ++n) { 19 | v.emplace_back(f); 20 | } 21 | for (auto& t : v) { 22 | t.join(); 23 | } 24 | std::cout << "Final counter value is " << cnt << '\n'; 25 | } -------------------------------------------------------------------------------- /src/main/memory_order/memory_order_counter_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Demo Relaxed ordering 7 | 8 | std::atomic cnt = {0}; 9 | 10 | void f() { 11 | for (int n = 0; n < 1000; ++n) { 12 | cnt.fetch_add(1, std::memory_order_relaxed); 13 | } 14 | } 15 | 16 | int main() { 17 | std::vector v; 18 | for (int n = 0; n < 10; ++n) { 19 | v.emplace_back(f); 20 | } 21 | for (auto& t : v) { 22 | t.join(); 23 | } 24 | std::cout << "Final counter value is " << cnt << '\n'; 25 | } -------------------------------------------------------------------------------- /src/main/memory_order/memory_order_main.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "src/lib/utility.h" 8 | 9 | // Demos relaxed memory ordering. 10 | // This will never produce r1 == r2 == 42 11 | int main() { 12 | std::vector threads; 13 | 14 | std::atomic r1, r2, x, y; 15 | r1 = 0; 16 | r2 = 0; 17 | x = 0; 18 | y = 0; 19 | 20 | std::cout << "r1: " << r1 << ", r2: " << r2 << std::endl; 21 | std::cout << "x: " << x << ", y: " << y << std::endl; 22 | 23 | threads.push_back(std::thread([&x, &r1, &y] { 24 | r1 = x.load(std::memory_order_relaxed); 25 | if (r1 == 42) { 26 | y.store(r1, std::memory_order_relaxed); 27 | } 28 | })); 29 | 30 | threads.push_back(std::thread([&x, &r2, &y] { 31 | r2 = y.load(std::memory_order_relaxed); 32 | if (r2 == 42) { 33 | x.store(42, std::memory_order_relaxed); 34 | } 35 | })); 36 | 37 | for (std::thread &t : threads) { 38 | if (t.joinable()) { 39 | t.join(); 40 | } 41 | } 42 | 43 | std::cout << "r1: " << r1 << ", r2: " << r2 << std::endl; 44 | std::cout << "x: " << x << ", y: " << y << std::endl; 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/memory_order/relaxed_4_threads_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::atomic x = {false}; 7 | std::atomic y = {false}; 8 | std::atomic z = {0}; 9 | 10 | void write_x() { x.store(true, std::memory_order_relaxed); } 11 | 12 | void write_y() { y.store(true, std::memory_order_relaxed); } 13 | 14 | void read_x_then_y() { 15 | while (!x.load(std::memory_order_relaxed)) 16 | ; 17 | if (y.load(std::memory_order_relaxed)) { 18 | ++z; 19 | } 20 | } 21 | 22 | void read_y_then_x() { 23 | while (!y.load(std::memory_order_relaxed)) 24 | ; 25 | if (x.load(std::memory_order_relaxed)) { 26 | ++z; 27 | } 28 | } 29 | 30 | int main() { 31 | std::vector threads; 32 | 33 | threads.push_back(std::thread(write_x)); 34 | threads.push_back(std::thread(write_y)); 35 | threads.push_back(std::thread(read_x_then_y)); 36 | threads.push_back(std::thread(read_y_then_x)); 37 | 38 | for (std::thread &t : threads) { 39 | if (t.joinable()) { 40 | t.join(); 41 | } 42 | } 43 | 44 | assert(z.load() != 0); // Can happen! 45 | } -------------------------------------------------------------------------------- /src/main/memory_order/relaxed_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | std::atomic msg("No message!"); 5 | std::atomic flag(0); 6 | std::atomic c; 7 | 8 | void store_msg_flag() { 9 | msg.store(std::string("Hello world"), std::memory_order_relaxed); 10 | flag.store(1, std::memory_order_relaxed); 11 | } 12 | 13 | void read_flag_msg() { 14 | while (flag.load(std::memory_order_relaxed) == 0) 15 | ; 16 | if (msg.load(std::memory_order_relaxed) != "Hello world") { 17 | c = true; 18 | } 19 | } 20 | 21 | int main() { 22 | auto t1 = std::thread(store_msg_flag); 23 | auto t2 = std::thread(read_flag_msg); 24 | t1.join(); 25 | t2.join(); 26 | assert(c.load() != true); // Can fire! 27 | return 0; 28 | } -------------------------------------------------------------------------------- /src/main/memory_order/relaxed_main_two_threads.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::atomic x{0}; 6 | std::atomic y{0}; 7 | std::atomic z; 8 | 9 | void write_x_y() { 10 | x.store(1, std::memory_order_relaxed); 11 | y.store(1, std::memory_order_release); 12 | } 13 | 14 | void read_y_x() { 15 | while (!y.load(std::memory_order_relaxed)) 16 | ; 17 | if (x.load(std::memory_order_relaxed)) { 18 | ++z; 19 | } 20 | } 21 | 22 | int main() { 23 | std::thread thread1(write_x_y); 24 | std::thread thread2(read_y_x); 25 | thread1.join(); 26 | thread2.join(); 27 | } -------------------------------------------------------------------------------- /src/main/memory_order/release_acquire_3t_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::vector data; 8 | std::atomic flag = {0}; 9 | 10 | void thread_1() { 11 | data.push_back(42); 12 | flag.store(1, std::memory_order_release); 13 | } 14 | 15 | void thread_2() { 16 | int expected = 1; 17 | while ( 18 | !flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) { 19 | std::cout << "Waiting in t2" << std::endl; 20 | expected = 1; 21 | } 22 | } 23 | 24 | void thread_3() { 25 | std::cout << "before load" << std::endl; 26 | 27 | while (flag.load(std::memory_order_acquire) < 2) { 28 | std::cout << "Waiting in t3" << std::endl; 29 | } 30 | assert(data.at(0) == 42); // will never fire 31 | } 32 | 33 | int main() { 34 | std::thread a(thread_1); 35 | std::thread b(thread_2); 36 | std::thread c(thread_3); 37 | a.join(); 38 | b.join(); 39 | c.join(); 40 | } -------------------------------------------------------------------------------- /src/main/memory_order/release_acquire_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::chrono::seconds 4 | #include 5 | #include 6 | #include 7 | 8 | std::atomic ptr; 9 | int data; 10 | 11 | // Demo showing release-acquire synchronization 12 | void producer() { 13 | std::string* p = new std::string("Hello"); 14 | data = 42; 15 | std::this_thread::sleep_for(std::chrono::seconds(3)); 16 | std::cout << "Before store" << std::endl; 17 | 18 | ptr.store(p, std::memory_order_release); 19 | } 20 | 21 | void consumer() { 22 | std::string* p2; 23 | std::cout << "Before load" << std::endl; 24 | 25 | // We block here until producer is done with store! 26 | while (!(p2 = ptr.load(std::memory_order_acquire))) { 27 | std::cout << "Waiting for producer" << std::endl; 28 | } 29 | 30 | std::cout << "After load" << std::endl; 31 | 32 | std::cout << "Before assert" << std::endl; 33 | std::cout << "*p2: " << *p2 << std::endl; 34 | assert(*p2 == "Hello"); // never fires 35 | assert(data == 42); // never fires 36 | } 37 | 38 | int main() { 39 | std::thread t1(producer); 40 | std::thread t2(consumer); 41 | t1.join(); 42 | t2.join(); 43 | } 44 | 45 | // std::atomic foo(0); 46 | 47 | // void set_foo(int x) { 48 | // std::this_thread::sleep_for(std::chrono::seconds(3)); 49 | 50 | // foo.store(x, std::memory_order_release); // set value atomically 51 | // } 52 | 53 | // void print_foo() { 54 | // int x; 55 | // do { 56 | // std::cout << "Before load" << std::endl; 57 | 58 | // x = foo.load(std::memory_order_acquire); // get value atomically 59 | // } while (x == 0); 60 | // std::cout << "foo: " << x << '\n'; 61 | // } 62 | 63 | // int main() { 64 | // std::thread first(print_foo); 65 | // std::thread second(set_foo, 10); 66 | // first.join(); 67 | // second.join(); 68 | // return 0; 69 | // } -------------------------------------------------------------------------------- /src/main/memory_order/release_acquire_main_simple.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::chrono::seconds 4 | #include 5 | #include 6 | #include 7 | 8 | std::atomic a{0}; 9 | 10 | int data; 11 | 12 | // Demo showing release-acquire synchronization 13 | void producer() { 14 | std::cout << "Before wait" << std::endl; 15 | 16 | std::this_thread::sleep_for(std::chrono::seconds(3)); 17 | std::cout << "Before store" << std::endl; 18 | 19 | a.store(1, std::memory_order_release); 20 | } 21 | 22 | void consumer() { 23 | std::cout << "Before load" << std::endl; 24 | 25 | int aa = a.load(std::memory_order_acquire); 26 | 27 | std::cout << "After load" << std::endl; 28 | 29 | std::cout << "aa: " << aa << std::endl; 30 | } 31 | 32 | int main() { 33 | std::thread t1(producer); 34 | std::thread t2(consumer); 35 | t1.join(); 36 | t2.join(); 37 | } 38 | 39 | // std::atomic foo(0); 40 | 41 | // void set_foo(int x) { 42 | // std::this_thread::sleep_for(std::chrono::seconds(3)); 43 | 44 | // foo.store(x, std::memory_order_release); // set value atomically 45 | // } 46 | 47 | // void print_foo() { 48 | // int x; 49 | // do { 50 | // std::cout << "Before load" << std::endl; 51 | 52 | // x = foo.load(std::memory_order_acquire); // get value atomically 53 | // } while (x == 0); 54 | // std::cout << "foo: " << x << '\n'; 55 | // } 56 | 57 | // int main() { 58 | // std::thread first(print_foo); 59 | // std::thread second(set_foo, 10); 60 | // first.join(); 61 | // second.join(); 62 | // return 0; 63 | // } -------------------------------------------------------------------------------- /src/main/memory_order/seq_cst_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::atomic x = {false}; 7 | std::atomic y = {false}; 8 | std::atomic z = {0}; 9 | 10 | void write_x() { x.store(true, std::memory_order_seq_cst); } 11 | 12 | void write_y() { y.store(true, std::memory_order_seq_cst); } 13 | 14 | void read_x_then_y() { 15 | while (!x.load(std::memory_order_seq_cst)) 16 | ; 17 | if (y.load(std::memory_order_seq_cst)) { 18 | ++z; 19 | } 20 | } 21 | 22 | void read_y_then_x() { 23 | while (!y.load(std::memory_order_seq_cst)) 24 | ; 25 | if (x.load(std::memory_order_seq_cst)) { 26 | ++z; 27 | } 28 | } 29 | 30 | int main() { 31 | std::vector threads; 32 | 33 | threads.push_back(std::thread(write_x)); 34 | threads.push_back(std::thread(write_y)); 35 | threads.push_back(std::thread(read_x_then_y)); 36 | threads.push_back(std::thread(read_y_then_x)); 37 | 38 | for (std::thread &t : threads) { 39 | if (t.joinable()) { 40 | t.join(); 41 | } 42 | } 43 | 44 | assert(z.load() != 0); // will never happen 45 | } -------------------------------------------------------------------------------- /src/main/memory_order/sequential_consistency_atomic_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Global variables 8 | std::atomic flag(0); 9 | std::atomic msg; 10 | int b = 0, x = 0, y = 0; 11 | std::string text; 12 | 13 | void f1() { 14 | x = 3; 15 | y += x; 16 | msg = "Hello world"; 17 | flag = 1; 18 | b += flag; 19 | } 20 | 21 | void f2() { 22 | text = "Hi"; 23 | while (flag != 1) 24 | ; 25 | text = msg; 26 | } 27 | 28 | int main() { 29 | std::vector threads; 30 | 31 | threads.push_back(std::thread(f1)); 32 | threads.push_back(std::thread(f2)); 33 | 34 | for (std::thread &t : threads) { 35 | if (t.joinable()) { 36 | t.join(); 37 | } 38 | } 39 | 40 | std::cout << "msg: " << msg << std::endl; 41 | std::cout << "b: " << b << ", y: " << y << std::endl; 42 | std::cout << "text: " << text << std::endl; 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/memory_order/sequential_consistency_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Global variables 7 | int flag = 0; 8 | int b = 0; 9 | int x = 0, y = 0; 10 | std::string msg, text; 11 | 12 | void f1() { 13 | x = 3; 14 | y += x; 15 | msg = "Hello world"; 16 | flag = 1; 17 | b += flag; 18 | } 19 | 20 | void f2() { 21 | text = "Hi"; 22 | while (flag != 1) 23 | ; 24 | text = msg; 25 | } 26 | 27 | int main() { 28 | std::vector threads; 29 | 30 | threads.push_back(std::thread(f1)); 31 | threads.push_back(std::thread(f2)); 32 | 33 | for (std::thread &t : threads) { 34 | if (t.joinable()) { 35 | t.join(); 36 | } 37 | } 38 | 39 | std::cout << "msg: " << msg << std::endl; 40 | std::cout << "b: " << b << ", y: " << y << std::endl; 41 | std::cout << "text: " << text << std::endl; 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/mutex/BUILD: -------------------------------------------------------------------------------- 1 | # https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary 2 | cc_binary( 3 | name = "main", 4 | srcs = ["main.cc"], 5 | deps = ["//src/lib:utility_lib"], 6 | ) 7 | 8 | cc_binary( 9 | name = "lock_unlock_main", 10 | srcs = ["lock_unlock_main.cc"], 11 | deps = ["//src/lib:utility_lib"], 12 | ) 13 | 14 | cc_binary( 15 | name = "lock_guard_main", 16 | srcs = ["lock_guard_main.cc"], 17 | deps = ["//src/lib:utility_lib"], 18 | ) 19 | 20 | cc_binary( 21 | name = "unique_lock_main", 22 | srcs = ["unique_lock_main.cc"], 23 | deps = ["//src/lib:utility_lib"], 24 | ) 25 | 26 | 27 | cc_binary( 28 | name = "shared_mutex_main", 29 | srcs = ["shared_mutex_main.cc"], 30 | deps = ["//src/lib:utility_lib"], 31 | ) 32 | 33 | 34 | cc_binary( 35 | name = "scoped_lock_main", 36 | srcs = ["scoped_lock_main.cc"], 37 | deps = ["//src/lib:utility_lib"], 38 | ) 39 | 40 | cc_binary( 41 | name = "conditional_var_main", 42 | srcs = ["conditional_var_main.cc"], 43 | deps = ["//src/lib:utility_lib"], 44 | ) 45 | 46 | cc_binary( 47 | name = "no_lock_main", 48 | srcs = ["no_lock_main.cc"], 49 | deps = ["//src/lib:utility_lib"], 50 | ) 51 | 52 | cc_binary( 53 | name = "lock_guard_multiple_mutex_main", 54 | srcs = ["lock_guard_multiple_mutex_main.cc"], 55 | deps = ["//src/lib:utility_lib"], 56 | ) 57 | 58 | 59 | cc_binary( 60 | name = "test_main", 61 | srcs = ["test_main.cc"], 62 | deps = ["//src/lib:utility_lib"], 63 | ) 64 | 65 | 66 | 67 | cc_binary( 68 | name = "mutex_seq_cst_main", 69 | srcs = ["mutex_seq_cst_main.cc"], 70 | deps = ["//src/lib:utility_lib"], 71 | ) 72 | 73 | 74 | 75 | cc_binary( 76 | name = "race_condition_main", 77 | srcs = ["race_condition_main.cc"], 78 | deps = ["//src/lib:utility_lib"], 79 | ) 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/mutex/lock_guard_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for mutex and lock_guard 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | std::mutex g_mutex; 18 | unsigned long g_counter; 19 | 20 | void Incrementer() { 21 | for (size_t i = 0; i < 100; i++) { 22 | std::lock_guard guard(g_mutex); 23 | g_counter++; 24 | } 25 | } 26 | 27 | int main() { 28 | std::vector threads; 29 | 30 | for (int i = 0; i < 100; i++) { 31 | threads.push_back(std::thread(Incrementer)); 32 | } 33 | 34 | for (std::thread &t : threads) { 35 | t.join(); 36 | } 37 | std::cout << "g_counter: " << g_counter << std::endl; 38 | 39 | assert(g_counter == 100 * 100); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/mutex/lock_guard_multiple_mutex_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for using multiple locks which may cause deadlock 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./s 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | std::mutex g_mutex1, g_mutex2; 18 | unsigned long g_counter; 19 | 20 | /** 21 | * Uses two locks in series. The order is the opposite of Incrementer_Bad2. This 22 | * may cause deadlock! 23 | */ 24 | void Incrementer_Bad1() { 25 | for (size_t i = 0; i < 100; i++) { 26 | std::lock_guard lock1(g_mutex1); 27 | std::lock_guard lock2(g_mutex2); 28 | g_counter++; 29 | } 30 | } 31 | 32 | /** 33 | * Uses two locks in series. The order is the opposite of Incrementer_Bad1. This 34 | * may cause deadlock! 35 | */ 36 | void Incrementer_Bad2() { 37 | for (size_t i = 0; i < 100; i++) { 38 | std::lock_guard lock2(g_mutex2); 39 | std::lock_guard lock1(g_mutex1); 40 | g_counter++; 41 | } 42 | } 43 | 44 | /** 45 | * Uses std::lock to lock two mutexes atomically. This avoids deadlock. 46 | */ 47 | void Incrementer_Better() { 48 | for (size_t i = 0; i < 100; i++) { 49 | // lock both mutexes without deadlock 50 | std::lock(g_mutex1, g_mutex2); 51 | 52 | // make sure both already-locked mutexes are unlocked at the end of scope 53 | std::lock_guard lock1(g_mutex1, std::adopt_lock); 54 | std::lock_guard lock2(g_mutex2, std::adopt_lock); 55 | g_counter++; 56 | } 57 | } 58 | 59 | /** 60 | * Uses std::scoped_lock to lock two mutexes atomically. This avoids deadlock. 61 | */ 62 | void Incrementer_Best() { 63 | for (size_t i = 0; i < 100; i++) { 64 | std::scoped_lock scoped_lock_name(g_mutex1, g_mutex2); 65 | g_counter++; 66 | } 67 | } 68 | 69 | int main() { 70 | std::vector threads; 71 | 72 | for (int i = 0; i < 100; i++) { 73 | // threads.push_back(std::thread(Incrementer_Bad1)); 74 | // threads.push_back(std::thread(Incrementer_Bad2)); 75 | threads.push_back(std::thread(Incrementer_Best)); 76 | } 77 | 78 | for (std::thread &t : threads) { 79 | t.join(); 80 | } 81 | std::cout << "g_counter: " << g_counter << std::endl; 82 | 83 | assert(g_counter == 100 * 100); 84 | } 85 | -------------------------------------------------------------------------------- /src/main/mutex/lock_unlock_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for mutex and locks 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | std::mutex g_mutex; 19 | unsigned long g_counter; 20 | 21 | void Incrementer() { 22 | for (size_t i = 0; i < 100; i++) { 23 | g_mutex.lock(); 24 | g_counter++; 25 | g_mutex.unlock(); 26 | } 27 | } 28 | 29 | int main() { 30 | std::map count; 31 | int N = 1000; 32 | for (int i = 0; i < N; i++) { 33 | g_counter = 0; 34 | 35 | std::vector threads; 36 | 37 | for (int i = 0; i < 100; i++) { 38 | threads.push_back(std::thread(Incrementer)); 39 | } 40 | 41 | for (std::thread &t : threads) { 42 | t.join(); 43 | } 44 | // std::cout << "g_counter: " << g_counter << std::endl; 45 | std::cout << g_counter << ", "; 46 | 47 | count[g_counter]++; 48 | } 49 | std::cout << std::endl; 50 | // Assert that we always get 100*100 (we count to 100, 100 times.) 51 | assert(count[100 * 100] == N); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/mutex/mutex_seq_cst_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include // For std::unique_lock 3 | #include 4 | #include 5 | #include 6 | 7 | std::mutex mutex; 8 | int x = 0, y = 0; 9 | 10 | int main() { 11 | std::thread A{[] { 12 | x = 1; 13 | std::lock_guard lg(std::mutex); 14 | y = 0; 15 | }}; 16 | std::thread B{[] { 17 | std::lock_guard lg(std::mutex); 18 | y = x + 2; 19 | }}; 20 | 21 | A.join(); 22 | B.join(); 23 | std::cout << "x: " << x << std::endl; 24 | std::cout << "y: " << y << std::endl; 25 | } -------------------------------------------------------------------------------- /src/main/mutex/no_lock_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for incorrectly using shared memory without mutex and locks 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "src/lib/utility.h" 19 | 20 | unsigned long g_counter = 0; 21 | 22 | void Incrementer() { 23 | for (size_t i = 0; i < 100; i++) { 24 | g_counter++; 25 | } 26 | } 27 | 28 | int main() { 29 | std::map count; 30 | int N = 1000; 31 | for (int i = 0; i < N; i++) { 32 | g_counter = 0; 33 | std::vector threads; 34 | 35 | for (int i = 0; i < 100; i++) { 36 | threads.push_back(std::thread(Incrementer)); 37 | } 38 | 39 | for (std::thread &t : threads) { 40 | t.join(); 41 | } 42 | // std::cout << "g_counter: " << g_counter << std::endl; 43 | std::cout << g_counter << ", "; 44 | } 45 | std::cout << std::endl; 46 | 47 | // Assert that we always got 100*100 48 | assert(count[100 * 100] == N); 49 | } -------------------------------------------------------------------------------- /src/main/mutex/race_condition_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for incorrectly using shared memory without mutex and locks 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | unsigned long g_x; 17 | 18 | int main() { 19 | for (int i = 0; i < 1000; i++) { 20 | auto t1 = std::thread([]() { g_x = 1; }); 21 | auto t2 = std::thread([]() { g_x = 2; }); 22 | t1.join(); 23 | t2.join(); 24 | std::cout << g_x << ", "; 25 | } 26 | std::cout << std::endl; 27 | } -------------------------------------------------------------------------------- /src/main/mutex/shared_mutex_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for shared_mutex and shared_lock 2 | // By Ari Saif 3 | // Run this using one of the following methods: 4 | // 1. With bazel: 5 | // bazel run --cxxopt='-std=c++17' \ 6 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 7 | // 2. With g++: 8 | // g++ -std=c++17 -lpthread \ 9 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | std::shared_mutex g_shared_mutex; 19 | unsigned long g_counter; 20 | 21 | void Incrementer() { 22 | for (size_t i = 0; i < 100; i++) { 23 | std::unique_lock ul(g_shared_mutex); 24 | g_counter++; 25 | } 26 | } 27 | 28 | void ImJustAReader() { 29 | for (size_t i = 0; i < 100; i++) { 30 | std::shared_lock sl(g_shared_mutex); 31 | std::cout << "g_counter: " << g_counter << std::endl; 32 | } 33 | } 34 | 35 | int main() { 36 | std::vector threads; 37 | 38 | for (int i = 0; i < 100; i++) { 39 | threads.push_back(std::thread(Incrementer)); 40 | threads.push_back(std::thread(ImJustAReader)); 41 | } 42 | 43 | for (std::thread &t : threads) { 44 | t.join(); 45 | } 46 | std::cout << "g_counter: " << g_counter << std::endl; 47 | 48 | assert(g_counter == 100 * 100); 49 | } -------------------------------------------------------------------------------- /src/main/mutex/test_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include // For std::unique_lock 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | mutex mA, mB, coutMutex; 9 | bool fA = false, fB = false; 10 | 11 | int main() { 12 | thread A{[] { 13 | lock_guard lock{mA}; 14 | fA = true; 15 | }}; 16 | thread B{[] { 17 | lock_guard lock{mB}; 18 | fB = true; 19 | }}; 20 | thread C{[] { // reads fA, then fB 21 | mA.lock(); 22 | const auto _1 = fA; 23 | mA.unlock(); 24 | mB.lock(); 25 | const auto _2 = fB; 26 | mB.unlock(); 27 | lock_guard lock{coutMutex}; 28 | cout << "Thread C: fA = " << _1 << ", fB = " << _2 << endl; 29 | }}; 30 | thread D{[] { // reads fB, then fA (i. e. vice versa) 31 | mB.lock(); 32 | const auto _3 = fB; 33 | mB.unlock(); 34 | mA.lock(); 35 | const auto _4 = fA; 36 | mA.unlock(); 37 | lock_guard lock{coutMutex}; 38 | cout << "Thread D: fA = " << _4 << ", fB = " << _3 << endl; 39 | }}; 40 | A.join(); 41 | B.join(); 42 | C.join(); 43 | D.join(); 44 | } -------------------------------------------------------------------------------- /src/main/mutex/unique_lock_main.cc: -------------------------------------------------------------------------------- 1 | // A demo for unique_lock: similar to lock_guard, but it can 2 | // be lock and unlock multiple times. 3 | // By Ari Saif 4 | // Run this using one of the following methods: 5 | // 1. With bazel: 6 | // bazel run --cxxopt='-std=c++17' \ 7 | // src/main/mutex:{THIS_FILE_NAME_WITHOUT_EXTENSION} 8 | // 2. With g++: 9 | // g++ -std=c++17 -lpthread \ 10 | // src/main/mutex/{THIS_FILE_NAME}.cc -I ./ 11 | #include 12 | #include 13 | #include // For std::unique_lock 14 | #include 15 | #include 16 | #include 17 | 18 | #include "src/lib/utility.h" 19 | 20 | std::mutex g_mutex; 21 | unsigned long g_counter; 22 | 23 | void Incrementer() { 24 | for (size_t i = 0; i < 100; i++) { 25 | std::unique_lock ul(g_mutex); 26 | g_counter++; 27 | ul.unlock(); 28 | std::cout << "Doing something non-critical..." << std::endl; 29 | ul.lock(); 30 | } 31 | } 32 | 33 | int main() { 34 | std::vector threads; 35 | 36 | for (int i = 0; i < 100; i++) { 37 | threads.push_back(std::thread(Incrementer)); 38 | } 39 | 40 | for (std::thread &t : threads) { 41 | t.join(); 42 | } 43 | std::cout << "g_counter: " << g_counter << std::endl; 44 | 45 | assert(g_counter == 100 * 100); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/number_of_cores.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "src/lib/utility.h" 7 | 8 | // A demo for creating two threads 9 | // Run this using one of the following methods: 10 | // 1. With bazel: bazel run src/main:number_of_cores 11 | // 2. With plain g++: g++ -std=c++17 -lpthread src/main/number_of_cores.cc -I ./ 12 | int main() { 13 | unsigned int c = std::thread::hardware_concurrency(); 14 | std::cout << " number of cores: " << c << std::endl; 15 | 16 | return 0; 17 | } -------------------------------------------------------------------------------- /src/main/vector_of_threads_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "src/lib/utility.h" 7 | 8 | // A demo for creating two threads 9 | // Run this using one of the following methods: 10 | // 1. With bazel: bazel run src/main:vector_of_threads_main 11 | // 2. With plain g++: g++ -std=c++17 -lpthread src/main/vector_of_threads_main.cc -I ./ 12 | int main() { 13 | const int number_of_threads = 1000; 14 | uint64_t number_of_elements = 1000 * 1000* 1000; 15 | uint64_t step = number_of_elements / number_of_threads; 16 | std::vector threads; 17 | std::vector partial_sums(number_of_threads); 18 | 19 | for (uint64_t i = 0; i < number_of_threads; i++) { 20 | threads.push_back(std::thread(AccumulateRange, std::ref(partial_sums[i]), 21 | i * step, (i + 1) * step)); 22 | } 23 | 24 | for (std::thread &t : threads) { 25 | if (t.joinable()) { 26 | t.join(); 27 | } 28 | } 29 | 30 | uint64_t total = 31 | std::accumulate(partial_sums.begin(), partial_sums.end(), uint64_t(0)); 32 | 33 | PrintVector(partial_sums); 34 | std::cout << "total: " << total << std::endl; 35 | 36 | return 0; 37 | } -------------------------------------------------------------------------------- /tests/BUILD: -------------------------------------------------------------------------------- 1 | cc_test( 2 | name = "tests", 3 | srcs = glob(["**/*.cc"]), 4 | deps = [ 5 | "//src/lib:SolutionLib", 6 | "@googletest//:gtest_main", 7 | ], 8 | ) 9 | 10 | cc_test( 11 | name = "vector_accumulator_test", 12 | srcs = ["vector_accumulator_test.cc"], 13 | deps = [ 14 | "//src/lib:VectorAccumulatorLib", 15 | "@googletest//:gtest_main", 16 | ], 17 | ) 18 | 19 | cc_test( 20 | name = "range_accumulator_test", 21 | srcs = ["range_accumulator_test.cc"], 22 | deps = [ 23 | "//src/lib:RangeAccumulatorLib", 24 | "@googletest//:gtest_main", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /tests/range_accumulator_test.cc: -------------------------------------------------------------------------------- 1 | #include "src/lib/range_accumulator.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gtest/gtest.h" 8 | 9 | std::mutex RangeAccumulator::_my_mutex; 10 | unsigned long RangeAccumulator::_mutex_sum(0); 11 | std::atomic RangeAccumulator::_atomic_sum(0); 12 | 13 | const unsigned long g_size = 1000000000; 14 | 15 | template 16 | void GenericTest(FT func, RT& result) { 17 | unsigned long in; 18 | unsigned long expected; 19 | 20 | // Check empty 21 | in = 0; 22 | expected = 0; 23 | 24 | for (int number_of_threads = 1; number_of_threads < 12; number_of_threads++) { 25 | RangeAccumulator::Driver(number_of_threads, func, 0); 26 | EXPECT_EQ(expected, result); 27 | } 28 | 29 | in = g_size; 30 | expected = in * (in - 1) / 2; 31 | 32 | for (int number_of_threads = 1; number_of_threads < 5; number_of_threads++) { 33 | RangeAccumulator::Driver(number_of_threads, func, in); 34 | EXPECT_EQ(expected, result); 35 | } 36 | } 37 | 38 | TEST(RangeAccumulatorTest, AtomicAccumulator) { 39 | GenericTest(RangeAccumulator::AtomicAccumulator, 40 | RangeAccumulator::_atomic_sum); 41 | } 42 | 43 | TEST(RangeAccumulatorTest, AtomicAccumulatorRelaxed) { 44 | GenericTest(RangeAccumulator::AtomicAccumulatorRelaxed, 45 | RangeAccumulator::_atomic_sum); 46 | } 47 | 48 | TEST(RangeAccumulatorTest, AtomicAccumulatorPartitionRelaxed) { 49 | GenericTest(RangeAccumulator::AtomicAccumulatorPartitionRelaxed, 50 | RangeAccumulator::_atomic_sum); 51 | } 52 | 53 | TEST(RangeAccumulatorTest, MutexAccumulatorPartition) { 54 | GenericTest(RangeAccumulator::MutexAccumulatorPartition, 55 | RangeAccumulator::_mutex_sum); 56 | } 57 | -------------------------------------------------------------------------------- /tests/solution_test.cc: -------------------------------------------------------------------------------- 1 | #include "src/lib/solution.h" 2 | #include "gtest/gtest.h" 3 | #include 4 | 5 | TEST(HelloWorldShould, ReturnHelloWorld) { 6 | Solution solution; 7 | std::string actual = solution.PrintHelloWorld(); 8 | std::string expected = "**** Hello World ****"; 9 | EXPECT_EQ(expected, actual); 10 | } -------------------------------------------------------------------------------- /tests/vector_accumulator_test.cc: -------------------------------------------------------------------------------- 1 | #include "src/lib/vector_accumulator.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gtest/gtest.h" 8 | 9 | std::mutex VectorAccumulator::_my_mutex; 10 | unsigned long VectorAccumulator::_mutex_sum(0); 11 | std::atomic VectorAccumulator::_atomic_sum(0); 12 | 13 | void FillWithRandomValues(std::vector& a) { 14 | // First create an instance of an engine. 15 | std::random_device rnd_device; 16 | // Specify the engine and distribution. 17 | std::mt19937 mersenne_engine{rnd_device()}; // Generates random integers 18 | std::uniform_int_distribution dist{0, 1}; 19 | 20 | auto gen = [&dist, &mersenne_engine]() { return dist(mersenne_engine); }; 21 | 22 | generate(std::begin(a), std::end(a), gen); 23 | } 24 | 25 | const unsigned long g_size = 100000000; 26 | 27 | template 28 | void GenericTest(FT func, RT& result) { 29 | std::vector in; 30 | unsigned long expected; 31 | 32 | // Check empty 33 | in = {}; 34 | expected = 0; 35 | 36 | for (int number_of_threads = 1; number_of_threads < 12; number_of_threads++) { 37 | VectorAccumulator::Driver(in, number_of_threads, func); 38 | EXPECT_EQ(expected, result); 39 | } 40 | 41 | // Check random 42 | in.resize(g_size); 43 | FillWithRandomValues(in); 44 | expected = std::accumulate(in.begin(), in.end(), 0); 45 | 46 | for (int number_of_threads = 1; number_of_threads < 5; number_of_threads++) { 47 | VectorAccumulator::Driver(in, number_of_threads, func); 48 | EXPECT_EQ(expected, result); 49 | } 50 | } 51 | 52 | TEST(VectorAccumulatorTest, AtomicAccumulator) { 53 | GenericTest(VectorAccumulator::AtomicAccumulator, VectorAccumulator::_atomic_sum); 54 | } 55 | 56 | TEST(VectorAccumulatorTest, AtomicAccumulatorRelaxed) { 57 | GenericTest(VectorAccumulator::AtomicAccumulatorRelaxed, VectorAccumulator::_atomic_sum); 58 | } 59 | 60 | TEST(VectorAccumulatorTest, AtomicAccumulatorPartitionRelaxed) { 61 | GenericTest(VectorAccumulator::AtomicAccumulatorPartitionRelaxed, 62 | VectorAccumulator::_atomic_sum); 63 | } 64 | 65 | TEST(VectorAccumulatorTest, MutexAccumulatorPartition) { 66 | GenericTest(VectorAccumulator::MutexAccumulatorPartition, VectorAccumulator::_mutex_sum); 67 | } 68 | -------------------------------------------------------------------------------- /youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourarash/multithreading_cpp/4069bad6a7a15693de5b6dbb9248b007e798c87c/youtube.png --------------------------------------------------------------------------------