Skip to content

Commit

Permalink
Implement CopyableMutex and CopyableAtomic (#1398)
Browse files Browse the repository at this point in the history
`CopyableMutex` behaves like `std::mutex` and `CopyableAtomic` behaves like `std:atomic`, except that they also have a copy constructor and copy assignment. For `CopyableAtomic` the current value is copied, for `CopyableMutex` the copy constructor copies nothing and the copy assignment does nothing.
  • Loading branch information
joka921 authored Jul 19, 2024
1 parent 2b8dd19 commit e12b59f
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 11 deletions.
14 changes: 3 additions & 11 deletions src/engine/Operation.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "parser/data/Variable.h"
#include "util/CancellationHandle.h"
#include "util/CompilerExtensions.h"
#include "util/CopyableSynchronization.h"

// forward declaration needed to break dependencies
class QueryExecutionTree;
Expand Down Expand Up @@ -337,17 +338,8 @@ class Operation {
// future.
LimitOffsetClause _limit;

// A mutex that can be "copied". The semantics are, that copying will create
// a new mutex. This is sufficient for applications like in
// `getInternallyVisibleVariableColumns()` where we just want to make a
// `const` member function that modifies a `mutable` member threadsafe.
struct CopyableMutex : std::mutex {
using std::mutex::mutex;
CopyableMutex(const CopyableMutex&) {}
};

// Mutex that protects the `variableToColumnMap_` below.
mutable CopyableMutex variableToColumnMapMutex_;
mutable ad_utility::CopyableMutex variableToColumnMapMutex_;
// Store the mapping from variables to column indices. `nullopt` means that
// this map has not yet been computed. This computation is typically performed
// in the const member function `getInternallyVisibleVariableColumns`, so we
Expand All @@ -361,7 +353,7 @@ class Operation {
externallyVisibleVariableToColumnMap_;

// Mutex that protects the `_resultSortedColumns` below.
mutable CopyableMutex _resultSortedColumnsMutex;
mutable ad_utility::CopyableMutex _resultSortedColumnsMutex;

// Store the list of columns by which the result is sorted.
mutable std::optional<vector<ColumnIndex>> _resultSortedColumns =
Expand Down
36 changes: 36 additions & 0 deletions src/util/CopyableSynchronization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2024, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author:
// Johannes Kalmbach ([email protected])

#pragma once

namespace ad_utility {
// A mutex that can be "copied". The semantics are that copying will create a
// new mutex. This is useful when we just want to make a `const` member function
// that modifies a `mutable` member threadsafe. Note that a copy-constructed
// `CopyableMutex` will be unlocked, even if the source was locked, and that
// copy assignment is a noop.
struct CopyableMutex : std::mutex {
using std::mutex::mutex;
CopyableMutex(const CopyableMutex&) {}
CopyableMutex& operator=(const CopyableMutex&) { return *this; }
};

// A `std::atomic` that can be "copied". The semantics are, that copying will
// create a new atomic that is initialized with the value being copied. This is
// useful if we want to store an atomic as a class member, but still want the
// class to be copyable.
template <typename T>
class CopyableAtomic : public std::atomic<T> {
using Base = std::atomic<T>;

public:
using Base::Base;
CopyableAtomic(const CopyableAtomic& rhs) : Base{rhs.load()} {}
CopyableAtomic& operator=(const CopyableAtomic& rhs) {
static_cast<Base&>(*this) = rhs.load();
return *this;
}
};
} // namespace ad_utility
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,5 @@ addLinkAndDiscoverTest(JThreadTest)
addLinkAndDiscoverTest(ChunkedForLoopTest)

addLinkAndDiscoverTest(FsstCompressorTest fsst)

addLinkAndDiscoverTest(CopyableSynchronizationTest)
37 changes: 37 additions & 0 deletions test/CopyableSynchronizationTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2024, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author:
// Johannes Kalmbach ([email protected])

#include <gmock/gmock.h>

#include "util/CopyableSynchronization.h"

using namespace ad_utility;

// _________________________________________________
TEST(CopyableSynchronization, CopyableMutex) {
// Not much to test here.
CopyableMutex m1;
m1.lock();
[[maybe_unused]] CopyableMutex m2{m1};
// m2 is still unlocked.
EXPECT_TRUE(m2.try_lock());
m2.unlock();
m1 = m2;
// m1 is still locked;
EXPECT_FALSE(m1.try_lock());
m1.unlock();
}

// _________________________________________________
TEST(CopyableSynchronization, CopyableAtomic) {
CopyableAtomic<int> i1 = 42;
CopyableAtomic<int> i2{i1};
EXPECT_EQ(i2, 42);
++i2;
EXPECT_EQ(i2, 43);
EXPECT_EQ(i1, 42);
i1 = i2;
EXPECT_EQ(i1, 43);
}

0 comments on commit e12b59f

Please sign in to comment.