concurrent and unit tests, concurrently linked list test (not production ready linked list :))

This commit is contained in:
Marko Budiselic 2016-06-06 23:57:16 +02:00
parent 894ef67bc0
commit c79706c249
12 changed files with 159 additions and 56 deletions

View File

@ -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

View File

@ -28,5 +28,8 @@ make
ctest
ctest -V
ctest -R test_name
ctest -R unit
ctest -R concurrent
ctest -R concurrent --parallel 4
```

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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()

View File

@ -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;
}