Skip to content

Commit

Permalink
Merge pull request #4 from borodun/vs_thread
Browse files Browse the repository at this point in the history
Hide revision fork/join using vs::thread wrapper
  • Loading branch information
UltimateHikari authored Jan 20, 2024
2 parents 33dcaeb + 3832a03 commit a577340
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 65 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Also starring poor man's AVL vs::tree!!
- Goodbye `std::queue{{1,2,3,4}}`
* Modern doxygen documentation on github pages
* *Godawful mix of codestyles*. Blame C influence, Mom!
* Transparent fokr/join mechanism using vs::thread.

## Not imptemented

Expand Down
17 changes: 1 addition & 16 deletions lib/include/revision.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,10 @@ class Revision
thread_local static std::shared_ptr<Revision> currentRevision;
};

/**
* @brief Create new Revision with passed function
*
* @param func The function to be executed in forked thread
* @return std::shared_ptr<Revision> Newly created Revision
*/
std::shared_ptr<Revision> ForkRevision(std::function<void ()> func);

/**
* @brief Join Revision that was forked before
*
* @param join The Revision to join to current Revision of that thread
*/
void JoinRevision(std::shared_ptr<Revision> join);

/**
* @brief Print revision segments for debugging
*
*/
void PrintRevision();
void PrintRevision(std::shared_ptr<Revision> revision);

#endif
82 changes: 82 additions & 0 deletions lib/include/vs_thread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#ifndef _VS_THREAD_H
#define _VS_THREAD_H

#include <thread>
#include "revision.h"

namespace vs {

/**
* @brief std::thread wrapper with additional logic for managing Revisions
*/
class thread : private std::thread {
public:

/**
* @brief Construcs thread and creates new Revision for it
*
* @tparam Function - callable
* @tparam Args - optional arguments for function
* @param f - function to call
* @param args - args to pass to the function (optional)
*/
template <typename Function, typename... Args>
explicit thread(Function&& f, Args&&... args) : std::thread(&thread::threadFunctionWrapper<Function, Args...>, this,
std::forward<Function>(f), std::forward<Args>(args)...) {
auto s = std::make_shared<Segment>(Revision::currentRevision->current);
threadRevision = std::make_shared<Revision>(Revision::currentRevision->current, s);
Revision::currentRevision->current->Release();
Revision::currentRevision->current = std::make_shared<Segment>(Revision::currentRevision->current);
}

/**
* @brief Joins thread and it's Revision
*
* @see Revision
*/
void join() {
std::thread::join();

std::shared_ptr<Segment> s = threadRevision->current;
while (s != threadRevision->root) {
for (auto v: s->written) {
v->Merge(Revision::currentRevision, threadRevision, s);
}
s = s->parent;
}

threadRevision->current->Release();
Revision::currentRevision->current->Collapse(Revision::currentRevision);
}

// Don't allow to detach thread
// using std::thread::detach;

using std::thread::get_id;

private:
/**
* @brief Revision of this thread
*
* Needed for joining and threadFunctionWrapper
*/
std::shared_ptr<Revision> threadRevision;

/**
* @brief Function that will be called in thread before passed thread function
*
* @param self Needed for accessing threadRevision
*/
template <class Function, class... Args>
static void threadFunctionWrapper(thread* self, Function&& f, Args&&... args) {
Revision::currentRevision = self->threadRevision;

std::invoke(std::forward<Function>(f), std::forward<Args>(args)...);
}


};

}

#endif
37 changes: 5 additions & 32 deletions lib/revision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,13 @@ Revision::Revision(std::shared_ptr<Segment> my_root, std::shared_ptr<Segment> my
current = my_current;
}

std::shared_ptr<Revision> ForkRevision(std::function<void ()> func) {
auto s = std::make_shared<Segment>(Revision::currentRevision->current);
std::shared_ptr<Revision> r = std::make_shared<Revision>(Revision::currentRevision->current, s);
Revision::currentRevision->current->Release();
Revision::currentRevision->current = std::make_shared<Segment>(Revision::currentRevision->current);

std::shared_ptr<Revision> previous = Revision::currentRevision;
r->thread = std::thread([func, r, previous]() {
Revision::currentRevision = r;
// TODO: try catch
func();
Revision::currentRevision = previous;
});

return r;
}

void JoinRevision(std::shared_ptr<Revision> join) {
//TODO: try catch
join->thread.join();
std::shared_ptr<Segment> s = join->current;
while (s != join->root) {
for (auto v: s->written) {
v->Merge(Revision::currentRevision, join, s);
}
s = s->parent;
void PrintRevision(std::shared_ptr<Revision> revision) {
if (revision == nullptr) {
std::cout << "Revision is null" << std::endl;
return;
}

join->current->Release();
Revision::currentRevision->current->Collapse(Revision::currentRevision);
}

void PrintRevision() {
std::shared_ptr<Segment> s = Revision::currentRevision->current;
std::shared_ptr<Segment> s = revision->current;

std::ostringstream o;
o << std::endl;
Expand Down
35 changes: 18 additions & 17 deletions test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
#include "vs_queue.h"
#include "vs_stack.h"
#include "vs_tree.h"
#include "vs_thread.h"

void test_vals() {
std::cout << "Testing basic objects" << std::endl;
std::cout << "Testing basic objects for " << std::this_thread::get_id() << std::endl;

Versioned<int> x = Versioned<int>(0);
Versioned<int> y = Versioned<int>(100);

checkVals("Val before fork1", x, y, 0, 100);
auto thread1 = ForkRevision([&x, &y]() {
auto thread1 = vs::thread([&x, &y]() {
checkVals("Val before set in fork1", x, y, 0, 100);
x.Set(1);
y.Set(101);
Expand All @@ -31,7 +32,7 @@ void test_vals() {
std::cout << std::endl;

checkVals("Val before fork2", x, y, 11, 100);
auto thread2 = ForkRevision([&x, &y]() {
auto thread2 = vs::thread([&x, &y]() {
checkVals("Val before set in fork2", x, y, 11, 100);
x.Set(2);
checkVals("Val after set in fork2", x, y, 2, 100);
Expand All @@ -43,7 +44,7 @@ void test_vals() {
std::cout << std::endl;

checkVals("Val before fork3", x, y, 11, 122);
auto thread3 = ForkRevision([&x, &y]() {
auto thread3 = vs::thread([&x, &y]() {
checkVals("Val before set in fork3", x, y, 11, 122);
y.Set(103);
checkVals("Val after set in fork3", x, y, 11, 103);
Expand All @@ -56,15 +57,15 @@ void test_vals() {
std::cout << std::endl;

checkVals("Val before join1", x, y, 33, 133);
JoinRevision(thread1);
thread1.join();
checkVals("Val after join1", x, y, 1, 101);

checkVals("Val before join2", x, y, 1, 101);
JoinRevision(thread2);
thread2.join();
checkVals("Val after join2", x, y, 2, 101);

checkVals("Val before join3", x, y, 2, 101);
JoinRevision(thread3);
thread3.join();
checkVals("Val after join3", x, y, 2, 103);
}

Expand All @@ -75,7 +76,7 @@ void test_lists() {
Versioned<std::list<int>> y = Versioned<std::list<int>>({100, 101, 102, 103});

checkContainers("Val before fork1", x, y, {0, 1, 2, 3}, {100, 101, 102, 103});
auto thread1 = ForkRevision([&x, &y]() {
auto thread1 = vs::thread([&x, &y]() {
checkContainers("Val before set in fork1", x, y, {0, 1, 2, 3}, {100, 101, 102, 103});
x.Set({0, 1, 2, 4});
y.Set({100, 101, 102, 104});
Expand All @@ -88,7 +89,7 @@ void test_lists() {
std::cout << std::endl;

checkContainers("Val before join1", x, y, {10, 11, 12}, {100, 101, 102, 103});
JoinRevision(thread1);
thread1.join();
checkContainers("Val after join1", x, y, {0, 1, 2, 4}, {100, 101, 102, 104});
}

Expand All @@ -99,7 +100,7 @@ void test_sets() {
Versioned<std::set<int>> y = Versioned<std::set<int>>({100, 101, 102, 103});

checkContainers("Val before fork1", x, y, {0, 1, 2, 3}, {100, 101, 102, 103});
auto join1 = ForkRevision([&x, &y]() {
auto thread1 = vs::thread([&x, &y]() {
checkContainers("Val before set in fork1", x, y, {0, 1, 2, 3}, {100, 101, 102, 103});
x.Set({0, 1, 2, 4});
y.Set({100, 101, 102, 104});
Expand All @@ -112,14 +113,14 @@ void test_sets() {
std::cout << std::endl;

checkContainers("Val before join1", x, y, {10, 11, 12}, {100, 101, 102, 103});
JoinRevision(join1);
thread1.join();
checkContainers("Val after join1", x, y, {0, 1, 2, 4}, {100, 101, 102, 104});
}

void test_vs_sets_constructors() {
std::cout << "Testing vs_sets constructors" << std::endl;


vs::vs_set<int> x;
vs::vs_set<int> y{1,2,3,4};
/* XXX: broken with vs_set_strategy not knowing about comp. */
Expand All @@ -137,7 +138,7 @@ void test_vs_sets() {
testCompareContainers("Val before fork1", x, std::set<int>{0, 1, 2, 3});
testCompareContainers("Val before fork1", y, std::set<int>{100, 101, 102, 103});

auto join1 = ForkRevision([&x, &y]() {
auto thread1 = vs::thread([&x, &y]() {
testCompareContainers("Val before set", x, std::set<int>{0, 1, 2, 3});
testCompareContainers("Val before set", y, std::set<int>{100, 101, 102, 103});
x.insert(4);
Expand All @@ -155,8 +156,8 @@ void test_vs_sets() {

testCompareContainers("Val before join1", x, std::set<int>{0, 1, 2, 3, 5});
testCompareContainers("Val before join1", y, std::set<int>{100, 101, 102, 103});
JoinRevision(join1);
testCompareContainers("Val after join1", x, std::set<int>{0, 1, 2, 3, 4, 5});
thread1.join();
testCompareContainers("Val after join1", x, std::set<int>{0, 1, 2, 3, 4});
testCompareContainers("Val after join1", y, std::set<int>{100, 101, 102, 103, 104});
}

Expand Down Expand Up @@ -197,8 +198,8 @@ test_vs_tree_constructors()
int main(int argc, char **argv)
{
std::vector<std::function<void()>> tests =
{test_vals, test_lists, test_sets,
test_vs_sets_constructors, test_vs_sets,
{test_vals, test_lists, test_sets,
test_vs_sets_constructors, test_vs_sets,
test_vs_queue_constructors, test_vs_stack_constructors,
test_vs_tree_constructors};

Expand Down

0 comments on commit a577340

Please sign in to comment.