From c79706c249494f80025e9f43010bcd941c28035a Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Mon, 6 Jun 2016 23:57:16 +0200 Subject: [PATCH] concurrent and unit tests, concurrently linked list test (not production ready linked list :)) --- CMakeLists.txt | 14 ++++++- README.md | 3 ++ src/data_structures/linked_list.hpp | 46 ++++++++++++++++++++ src/data_structures/sllist.hpp | 37 ----------------- tests/CMakeLists.txt | 55 ++++++++++++++++-------- tests/concurrent/linked_list.cpp | 60 +++++++++++++++++++++++++++ tests/{ => concurrent}/timer.cpp | 0 tests/{ => unit}/cypher_traversal.cpp | 0 tests/{ => unit}/db_index.cpp | 0 tests/{ => unit}/dynamic_bitset.cpp | 0 tests/{ => unit}/lockfree_hashmap.cpp | 0 tests/{ => unit}/skiplist.cpp | 0 12 files changed, 159 insertions(+), 56 deletions(-) create mode 100644 src/data_structures/linked_list.hpp delete mode 100644 src/data_structures/sllist.hpp create mode 100644 tests/concurrent/linked_list.cpp rename tests/{ => concurrent}/timer.cpp (100%) rename tests/{ => unit}/cypher_traversal.cpp (100%) rename tests/{ => unit}/db_index.cpp (100%) rename tests/{ => unit}/dynamic_bitset.cpp (100%) rename tests/{ => unit}/lockfree_hashmap.cpp (100%) rename tests/{ => unit}/skiplist.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e859d5111..28acf164b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,25 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y") # functions +# prints all included directories function(list_includes) - get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + PROPERTY INCLUDE_DIRECTORIES) foreach(dir ${dirs}) message(STATUS "dir='${dir}'") endforeach() endfunction(list_includes) +# get file names from list of file paths +function(get_file_names file_paths file_names) + set(file_names "") + foreach(file_path ${file_paths}) + get_filename_component (file_name ${file_path} NAME_WE) + list(APPEND file_names ${file_name}) + endforeach() + set(file_names "${file_names}" PARENT_SCOPE) +endfunction() + # custom targets # move test data data to the build directory diff --git a/README.md b/README.md index aa02a42cd..9c4f7b0c5 100644 --- a/README.md +++ b/README.md @@ -28,5 +28,8 @@ make ctest ctest -V ctest -R test_name +ctest -R unit +ctest -R concurrent +ctest -R concurrent --parallel 4 ``` diff --git a/src/data_structures/linked_list.hpp b/src/data_structures/linked_list.hpp new file mode 100644 index 000000000..4d05cc4ef --- /dev/null +++ b/src/data_structures/linked_list.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include <list> + +#include "threading/sync/lockable.hpp" +#include "threading/sync/spinlock.hpp" + +template <typename value_type, typename lock_type = SpinLock> +class LinkedList : public Lockable<lock_type> +{ +public: + std::size_t size() const + { + auto guard = this->acquire_unique(); + return data.size(); + } + + void push_front(const value_type &value) + { + auto guard = this->acquire_unique(); + data.push_front(value); + } + + void push_front(value_type &&value) + { + auto guard = this->acquire_unique(); + data.push_front(std::forward<value_type>(value)); + } + + void pop_front() + { + auto guard = this->acquire_unique(); + data.pop_front(); + } + + // value_type& as return value + // would not be concurrent + value_type front() + { + auto guard = this->acquire_unique(); + return data.front(); + } + +private: + std::list<value_type> data; +}; diff --git a/src/data_structures/sllist.hpp b/src/data_structures/sllist.hpp deleted file mode 100644 index 1cada4c80..000000000 --- a/src/data_structures/sllist.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include <list> - -#include "sync/spinlock.hpp" -#include "sync/lockable.hpp" - -template <class T> -class SpinLockedList : Lockable<SpinLock> -{ -public: - - void push_front(T item) - { - auto guard = this->acquire(); - head = new Node(item, head); - } - - bool remove(const T&) - { - // HM! - } - -private: - struct Node : Lockable<SpinLock> - { - Node(T item, Node* next) : item(item), next(next) {} - - T item; - Node* next; - }; - - Node* head; - - // TODO add removed items to a list for garbage collection - // std::list<Node*> removed; -}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5aa4ebb75..34fe7fef9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,25 +21,44 @@ set(catch_source_dir ${source_dir}) include_directories(${catch_source_dir}/include) -# find tests -file(GLOB_RECURSE test_files ${CMAKE_HOME_DIRECTORY}/tests/*.cpp) -set(tests "") -foreach(test_file ${test_files}) - get_filename_component(test_name ${test_file} NAME_WE) - list(APPEND tests ${test_name}) -endforeach() -MESSAGE(STATUS "Available tests are: ${tests}") +## UNIT TESTS -# copy test data -file(COPY ${CMAKE_SOURCE_DIR}/tests/data DESTINATION ${CMAKE_BINARY_DIR}/tests) +# find unit tests +file(GLOB_RECURSE unit_test_files ${CMAKE_HOME_DIRECTORY}/tests/unit/*.cpp) +get_file_names("${unit_test_files}" file_names) +set(unit_test_names "${file_names}") +message(STATUS "Available unit tests are: ${unit_test_names}") -# build tests -foreach(test ${tests}) - add_executable(${test} ${test}.cpp) +# copy unit test data +file(COPY ${CMAKE_SOURCE_DIR}/tests/data + DESTINATION ${CMAKE_BINARY_DIR}/tests) + +# build unit tests +foreach(test ${unit_test_names}) + set(test_name unit_${test}) + add_executable(${test_name} unit/${test}.cpp) # TODO: separate dependencies - target_link_libraries(${test} stdc++fs) - target_link_libraries(${test} cypher_lib) - target_link_libraries(${test} Threads::Threads) - add_test(NAME ${test} COMMAND ${test}) - set_property(TARGET ${test} PROPERTY CXX_STANDARD 14) + target_link_libraries(${test_name} stdc++fs) + target_link_libraries(${test_name} cypher_lib) + target_link_libraries(${test_name} Threads::Threads) + add_test(NAME ${test_name} COMMAND ${test_name}) + set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 14) +endforeach() + +## CONCURRENCY TESTS + +# find concurrency tests +file(GLOB_RECURSE concurrency_test_files + ${CMAKE_HOME_DIRECTORY}/tests/concurrent/*.cpp) +get_file_names("${concurrency_test_files}" file_names) +set(concurrency_test_names "${file_names}") +message(STATUS "Available concurrency tests are: ${concurrency_test_names}") + +# build concurrency tests +foreach(test ${concurrency_test_names}) + set(test_name concurrent_${test}) + add_executable(${test_name} concurrent/${test}.cpp) + target_link_libraries(${test_name} Threads::Threads) + add_test(NAME ${test_name} COMMAND ${test_name}) + set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 14) endforeach() diff --git a/tests/concurrent/linked_list.cpp b/tests/concurrent/linked_list.cpp new file mode 100644 index 000000000..4ef5de29f --- /dev/null +++ b/tests/concurrent/linked_list.cpp @@ -0,0 +1,60 @@ +#include <cassert> +#include <iostream> +#include <thread> + +#include "data_structures/linked_list.hpp" + +using std::cout; +using std::endl; + +template <typename list_type> +void test_concurrent_list_access(list_type &list, std::size_t size) +{ + // test concurrent access + for (int i = 0; i < 1000000; ++i) { + + std::thread t1([&list] { + list.push_front(1); + list.pop_front(); + }); + + std::thread t2([&list] { + list.push_front(2); + list.pop_front(); + }); + + t1.join(); + t2.join(); + + assert(list.size() == size); + } +} + +int main() +{ + LinkedList<int> list; + + // push & pop operations + list.push_front(10); + list.push_front(20); + auto a = list.front(); + assert(a == 20); + list.pop_front(); + a = list.front(); + assert(a == 10); + list.pop_front(); + assert(list.size() == 0); + + // concurrent test + LinkedList<int> concurrent_list; + concurrent_list.push_front(1); + concurrent_list.push_front(1); + std::list<int> no_concurrent_list; + no_concurrent_list.push_front(1); + no_concurrent_list.push_front(1); + + test_concurrent_list_access(concurrent_list, 2); + // test_concurrent_list_access(no_concurrent_list, 2); + + return 0; +} diff --git a/tests/timer.cpp b/tests/concurrent/timer.cpp similarity index 100% rename from tests/timer.cpp rename to tests/concurrent/timer.cpp diff --git a/tests/cypher_traversal.cpp b/tests/unit/cypher_traversal.cpp similarity index 100% rename from tests/cypher_traversal.cpp rename to tests/unit/cypher_traversal.cpp diff --git a/tests/db_index.cpp b/tests/unit/db_index.cpp similarity index 100% rename from tests/db_index.cpp rename to tests/unit/db_index.cpp diff --git a/tests/dynamic_bitset.cpp b/tests/unit/dynamic_bitset.cpp similarity index 100% rename from tests/dynamic_bitset.cpp rename to tests/unit/dynamic_bitset.cpp diff --git a/tests/lockfree_hashmap.cpp b/tests/unit/lockfree_hashmap.cpp similarity index 100% rename from tests/lockfree_hashmap.cpp rename to tests/unit/lockfree_hashmap.cpp diff --git a/tests/skiplist.cpp b/tests/unit/skiplist.cpp similarity index 100% rename from tests/skiplist.cpp rename to tests/unit/skiplist.cpp