Make mg-utils compile (not working properly yet)
This commit is contained in:
parent
32facac135
commit
fbc2b4c99b
@ -9,4 +9,17 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
namespace memgraph::utils::timer {}
|
||||
#include "utils/async_timer.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
AsyncTimer::AsyncTimer(double seconds) {
|
||||
// TODO(gitbuda): Implement AsyncTimer constructor.
|
||||
}
|
||||
|
||||
bool AsyncTimer::IsExpired() const noexcept {
|
||||
// TODO(gitbuda): Implement AsyncTimer::IsExpired
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace memgraph::utils
|
30
src/utils/async_timer.hpp
Normal file
30
src/utils/async_timer.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
class AsyncTimer {
|
||||
public:
|
||||
explicit AsyncTimer(double seconds);
|
||||
AsyncTimer() = default;
|
||||
~AsyncTimer() = default;
|
||||
AsyncTimer(AsyncTimer &&other) = default;
|
||||
AsyncTimer &operator=(AsyncTimer &&other) = default;
|
||||
AsyncTimer(const AsyncTimer &) = delete;
|
||||
AsyncTimer &operator=(const AsyncTimer &) = delete;
|
||||
|
||||
// Returns false if the object isn't associated with any timer.
|
||||
bool IsExpired() const noexcept;
|
||||
};
|
||||
|
||||
}
|
@ -60,7 +60,7 @@ void Reader::TryInitializeHeader() {
|
||||
}
|
||||
|
||||
if (header->empty()) {
|
||||
throw CsvReadException("CSV file {} empty!", path_);
|
||||
throw CsvReadException("CSV file {} empty!", path_.string());
|
||||
}
|
||||
|
||||
number_of_columns_ = header->size();
|
||||
|
@ -52,7 +52,7 @@ void EnsureDirOrDie(const std::filesystem::path &dir) {
|
||||
MG_ASSERT(EnsureDir(dir),
|
||||
"Couldn't create directory '{}' due to a permission issue or the "
|
||||
"path exists and isn't a directory!",
|
||||
dir);
|
||||
dir.string());
|
||||
}
|
||||
|
||||
bool DirExists(const std::filesystem::path &dir) {
|
||||
@ -82,7 +82,8 @@ bool RenamePath(const std::filesystem::path &src, const std::filesystem::path &d
|
||||
return !error_code;
|
||||
}
|
||||
|
||||
static_assert(std::is_same_v<off_t, ssize_t>, "off_t must fit into ssize_t!");
|
||||
// TODO(gitbuda): Port static_assert(off_t = ssize_t)
|
||||
// static_assert(std::is_same_v<off_t, ssize_t>, "off_t must fit into ssize_t!");
|
||||
|
||||
InputFile::~InputFile() { Close(); }
|
||||
|
||||
@ -248,7 +249,7 @@ void InputFile::Close() noexcept {
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
spdlog::error("While trying to close {} an error occured: {} ({})", path_, strerror(errno), errno);
|
||||
spdlog::error("While trying to close {} an error occured: {} ({})", path_.string(), strerror(errno), errno);
|
||||
}
|
||||
|
||||
fd_ = -1;
|
||||
@ -321,7 +322,7 @@ void OutputFile::Open(const std::filesystem::path &path, Mode mode) {
|
||||
MG_ASSERT(!IsOpen(),
|
||||
"While trying to open {} for writing the database"
|
||||
" used a handle that already has {} opened in it!",
|
||||
path, path_);
|
||||
path.string(), path_.string());
|
||||
path_ = path;
|
||||
written_since_last_sync_ = 0;
|
||||
|
||||
@ -341,7 +342,7 @@ void OutputFile::Open(const std::filesystem::path &path, Mode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
MG_ASSERT(fd_ != -1, "While trying to open {} for writing an error occured: {} ({})", path_, strerror(errno), errno);
|
||||
MG_ASSERT(fd_ != -1, "While trying to open {} for writing an error occured: {} ({})", path_.string(), strerror(errno), errno);
|
||||
}
|
||||
|
||||
bool OutputFile::IsOpen() const { return fd_ != -1; }
|
||||
@ -390,7 +391,7 @@ size_t OutputFile::SeekFile(const Position position, const ssize_t offset) {
|
||||
if (pos == -1 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
MG_ASSERT(pos >= 0, "While trying to set the position in {} an error occured: {} ({})", path_, strerror(errno),
|
||||
MG_ASSERT(pos >= 0, "While trying to set the position in {} an error occured: {} ({})", path_.string(), strerror(errno),
|
||||
errno);
|
||||
return pos;
|
||||
}
|
||||
@ -467,7 +468,7 @@ void OutputFile::Sync() {
|
||||
MG_ASSERT(ret == 0,
|
||||
"While trying to sync {}, an error occurred: {} ({}). Possibly {} "
|
||||
"bytes from previous write calls were lost.",
|
||||
path_, strerror(errno), errno, written_since_last_sync_);
|
||||
path_.string(), strerror(errno), errno, written_since_last_sync_);
|
||||
|
||||
// Reset the counter.
|
||||
written_since_last_sync_ = 0;
|
||||
@ -492,7 +493,7 @@ void OutputFile::Close() noexcept {
|
||||
MG_ASSERT(ret == 0,
|
||||
"While trying to close {}, an error occurred: {} ({}). Possibly {} "
|
||||
"bytes from previous write calls were lost.",
|
||||
path_, strerror(errno), errno, written_since_last_sync_);
|
||||
path_.string(), strerror(errno), errno, written_since_last_sync_);
|
||||
|
||||
fd_ = -1;
|
||||
written_since_last_sync_ = 0;
|
||||
@ -512,7 +513,7 @@ void OutputFile::FlushBufferInternal() {
|
||||
MG_ASSERT(buffer_position_ <= kFileBufferSize,
|
||||
"While trying to write to {} more file was written to the "
|
||||
"buffer than the buffer has space!",
|
||||
path_);
|
||||
path_.string());
|
||||
|
||||
auto *buffer = buffer_;
|
||||
auto buffer_position = buffer_position_.load();
|
||||
@ -526,7 +527,7 @@ void OutputFile::FlushBufferInternal() {
|
||||
"while trying to write to {} an error occurred: {} ({}). "
|
||||
"Possibly {} bytes of data were lost from this call and "
|
||||
"possibly {} bytes were lost from previous calls.",
|
||||
path_, strerror(errno), errno, buffer_position_, written_since_last_sync_);
|
||||
path_.string(), strerror(errno), errno, buffer_position_, written_since_last_sync_);
|
||||
|
||||
buffer_position -= written;
|
||||
buffer += written;
|
||||
|
@ -10,14 +10,17 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "utils/file_locker.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
namespace {
|
||||
void DeleteFromSystem(const std::filesystem::path &path) {
|
||||
if (!utils::DeleteFile(path)) {
|
||||
spdlog::warn("Couldn't delete file {}!", path);
|
||||
spdlog::warn("Couldn't delete file {}!", path.string());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@ -25,7 +28,7 @@ void DeleteFromSystem(const std::filesystem::path &path) {
|
||||
////// FileRetainer //////
|
||||
void FileRetainer::DeleteFile(const std::filesystem::path &path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
spdlog::info("File {} doesn't exist.", path);
|
||||
spdlog::info("File {} doesn't exist.", path.string());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
|
129
src/utils/lock/linux_rw_lock.hpp
Normal file
129
src/utils/lock/linux_rw_lock.hpp
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
/// @file
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils::lock {
|
||||
|
||||
/// A wrapper around `pthread_rwlock_t`, useful because it is not possible to
|
||||
/// choose read or write priority for `std::shared_mutex`.
|
||||
class RWLock {
|
||||
public:
|
||||
/// By passing the appropriate parameter to the `RWLock` constructor, it is
|
||||
/// possible to control the behavior of `RWLock` while shared lock is held. If
|
||||
/// the priority is set to `READ`, new shared (read) locks can be obtained
|
||||
/// even though there is a thread waiting for an exclusive (write) lock, which
|
||||
/// can lead to writer starvation. If the priority is set to `WRITE`, readers
|
||||
/// will be blocked from obtaining new shared locks while there are writers
|
||||
/// waiting, which can lead to reader starvation.
|
||||
enum class Priority { READ, WRITE };
|
||||
|
||||
/// Construct a RWLock object with chosen priority. See comment above
|
||||
/// `RWLockPriority` for details.
|
||||
explicit RWLock(Priority priority) {
|
||||
pthread_rwlockattr_t attr;
|
||||
|
||||
MG_ASSERT(pthread_rwlockattr_init(&attr) == 0, "Couldn't initialize utils::RWLock!");
|
||||
|
||||
switch (priority) {
|
||||
case Priority::READ:
|
||||
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_READER_NP);
|
||||
break;
|
||||
case Priority::WRITE:
|
||||
// There is also `PTHREAD_RWLOCK_PREFER_WRITER_NP` but it is not
|
||||
// providing the desired behavior.
|
||||
//
|
||||
// From `man 7 pthread_rwlockattr_setkind_np`:
|
||||
// "Setting the value read-write lock kind to
|
||||
// PTHREAD_RWLOCK_PREFER_WRITER_NP results in the same behavior as
|
||||
// setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a
|
||||
// reader thread holds the lock, the thread holding a write lock will be
|
||||
// starved. Setting the lock kind to
|
||||
// PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP allows writers to run,
|
||||
// but, as the name implies a writer may not lock recursively."
|
||||
//
|
||||
// For this reason, `RWLock` should not be used recursively.
|
||||
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
break;
|
||||
}
|
||||
|
||||
MG_ASSERT(pthread_rwlock_init(&lock_, &attr) == 0, "Couldn't initialize utils::RWLock!");
|
||||
pthread_rwlockattr_destroy(&attr);
|
||||
}
|
||||
|
||||
RWLock(const RWLock &) = delete;
|
||||
RWLock &operator=(const RWLock &) = delete;
|
||||
RWLock(RWLock &&) = delete;
|
||||
RWLock &operator=(RWLock &&) = delete;
|
||||
|
||||
~RWLock() { pthread_rwlock_destroy(&lock_); }
|
||||
|
||||
void lock() { MG_ASSERT(pthread_rwlock_wrlock(&lock_) == 0, "Couldn't lock utils::RWLock!"); }
|
||||
|
||||
bool try_lock() {
|
||||
int err = pthread_rwlock_trywrlock(&lock_);
|
||||
if (err == 0) return true;
|
||||
MG_ASSERT(err == EBUSY, "Couldn't try lock utils::RWLock!");
|
||||
return false;
|
||||
}
|
||||
|
||||
void unlock() { MG_ASSERT(pthread_rwlock_unlock(&lock_) == 0, "Couldn't unlock utils::RWLock!"); }
|
||||
|
||||
void lock_shared() {
|
||||
int err;
|
||||
while (true) {
|
||||
err = pthread_rwlock_rdlock(&lock_);
|
||||
if (err == 0) {
|
||||
return;
|
||||
} else if (err == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
LOG_FATAL("Couldn't lock shared utils::RWLock!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_shared() {
|
||||
int err;
|
||||
while (true) {
|
||||
err = pthread_rwlock_tryrdlock(&lock_);
|
||||
if (err == 0) {
|
||||
return true;
|
||||
} else if (err == EBUSY) {
|
||||
return false;
|
||||
} else if (err == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
LOG_FATAL("Couldn't try lock shared utils::RWLock!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared() { MG_ASSERT(pthread_rwlock_unlock(&lock_) == 0, "Couldn't unlock shared utils::RWLock!"); }
|
||||
|
||||
private:
|
||||
pthread_rwlock_t lock_ = PTHREAD_RWLOCK_INITIALIZER;
|
||||
};
|
||||
|
||||
class WritePrioritizedRWLock final : public RWLock {
|
||||
public:
|
||||
WritePrioritizedRWLock() : RWLock{Priority::WRITE} {};
|
||||
};
|
||||
|
||||
} // namespace memgraph::utils
|
78
src/utils/lock/linux_spin_lock.hpp
Normal file
78
src/utils/lock/linux_spin_lock.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils::lock {
|
||||
|
||||
/// This class is a wrapper around the `pthread_spinlock_t`. It provides a
|
||||
/// generic spin lock. The lock should be used in cases where you know that the
|
||||
/// lock will be contended only for short periods of time. This lock doesn't
|
||||
/// make any kernel calls (like sleep, or context switching) during its wait for
|
||||
/// the lock to be acquired. This property is only useful when the lock will be
|
||||
/// held for short periods of time and you don't want to introduce the extra
|
||||
/// delays of a sleep or context switch. On the assembly level
|
||||
/// `pthread_spinlock_t` is optimized to use less power, reduce branch
|
||||
/// mispredictions, etc... The explanation can be seen here:
|
||||
/// https://stackoverflow.com/questions/26583433/c11-implementation-of-spinlock-using-atomic/29195378#29195378
|
||||
/// https://software.intel.com/en-us/node/524249
|
||||
class SpinLock {
|
||||
public:
|
||||
SpinLock() {
|
||||
// `pthread_spin_init` returns -1 only when there isn't enough memory to
|
||||
// initialize the lock. That should never occur because the
|
||||
// `pthread_spinlock_t` is an `int` and memory isn't allocated by this init.
|
||||
// The message is probably here to suit all other platforms...
|
||||
MG_ASSERT(pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
}
|
||||
|
||||
SpinLock(SpinLock &&other) noexcept : lock_(other.lock_) {
|
||||
MG_ASSERT(pthread_spin_init(&other.lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
}
|
||||
|
||||
SpinLock &operator=(SpinLock &&other) noexcept {
|
||||
MG_ASSERT(pthread_spin_destroy(&lock_) == 0, "Couldn't destruct utils::SpinLock!");
|
||||
lock_ = other.lock_;
|
||||
MG_ASSERT(pthread_spin_init(&other.lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
return *this;
|
||||
}
|
||||
|
||||
SpinLock(const SpinLock &) = delete;
|
||||
SpinLock &operator=(const SpinLock &) = delete;
|
||||
|
||||
~SpinLock() { MG_ASSERT(pthread_spin_destroy(&lock_) == 0, "Couldn't destruct utils::SpinLock!"); }
|
||||
|
||||
void lock() {
|
||||
// `pthread_spin_lock` returns -1 only when there is a deadlock detected
|
||||
// (errno EDEADLOCK).
|
||||
MG_ASSERT(pthread_spin_lock(&lock_) == 0, "Couldn't lock utils::SpinLock!");
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
// `pthread_spin_trylock` returns -1 only when the lock is already locked
|
||||
// (errno EBUSY).
|
||||
return pthread_spin_trylock(&lock_) == 0;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// `pthread_spin_unlock` has no documented error codes that it could return,
|
||||
// so any error is a fatal error.
|
||||
MG_ASSERT(pthread_spin_unlock(&lock_) == 0, "Couldn't unlock utils::SpinLock!");
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_spinlock_t lock_;
|
||||
};
|
||||
} // namespace memgraph::utils
|
@ -18,7 +18,7 @@
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
static_assert(std::is_same_v<uint64_t, unsigned long>,
|
||||
static_assert(std::is_same_v<uint64_t, unsigned long long>,
|
||||
"utils::Log requires uint64_t to be implemented as unsigned long.");
|
||||
|
||||
/// This function computes the log2 function on integer types. It is faster than
|
||||
|
@ -87,7 +87,8 @@ void MonotonicBufferResource::Release() {
|
||||
|
||||
void *MonotonicBufferResource::DoAllocate(size_t bytes, size_t alignment) {
|
||||
static_assert(std::is_same_v<size_t, uintptr_t>);
|
||||
static_assert(std::is_same_v<size_t, uint64_t>);
|
||||
// TODO(gitbuda): Fix huge static_assert that size_t == uint64_t
|
||||
//static_assert(std::is_same_v<size_t, uint64_t>);
|
||||
auto push_current_buffer = [this, bytes, alignment](size_t next_size) {
|
||||
// Set size so that the bytes fit.
|
||||
const size_t size = next_size > bytes ? next_size : bytes;
|
||||
|
@ -12,113 +12,47 @@
|
||||
/// @file
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
/// A wrapper around `pthread_rwlock_t`, useful because it is not possible to
|
||||
/// choose read or write priority for `std::shared_mutex`.
|
||||
class RWLock {
|
||||
public:
|
||||
/// By passing the appropriate parameter to the `RWLock` constructor, it is
|
||||
/// possible to control the behavior of `RWLock` while shared lock is held. If
|
||||
/// the priority is set to `READ`, new shared (read) locks can be obtained
|
||||
/// even though there is a thread waiting for an exclusive (write) lock, which
|
||||
/// can lead to writer starvation. If the priority is set to `WRITE`, readers
|
||||
/// will be blocked from obtaining new shared locks while there are writers
|
||||
/// waiting, which can lead to reader starvation.
|
||||
enum class Priority { READ, WRITE };
|
||||
|
||||
/// Construct a RWLock object with chosen priority. See comment above
|
||||
/// `RWLockPriority` for details.
|
||||
explicit RWLock(Priority priority) {
|
||||
pthread_rwlockattr_t attr;
|
||||
|
||||
MG_ASSERT(pthread_rwlockattr_init(&attr) == 0, "Couldn't initialize utils::RWLock!");
|
||||
|
||||
switch (priority) {
|
||||
case Priority::READ:
|
||||
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_READER_NP);
|
||||
break;
|
||||
case Priority::WRITE:
|
||||
// There is also `PTHREAD_RWLOCK_PREFER_WRITER_NP` but it is not
|
||||
// providing the desired behavior.
|
||||
//
|
||||
// From `man 7 pthread_rwlockattr_setkind_np`:
|
||||
// "Setting the value read-write lock kind to
|
||||
// PTHREAD_RWLOCK_PREFER_WRITER_NP results in the same behavior as
|
||||
// setting the value to PTHREAD_RWLOCK_PREFER_READER_NP. As long as a
|
||||
// reader thread holds the lock, the thread holding a write lock will be
|
||||
// starved. Setting the lock kind to
|
||||
// PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP allows writers to run,
|
||||
// but, as the name implies a writer may not lock recursively."
|
||||
//
|
||||
// For this reason, `RWLock` should not be used recursively.
|
||||
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
break;
|
||||
}
|
||||
|
||||
MG_ASSERT(pthread_rwlock_init(&lock_, &attr) == 0, "Couldn't initialize utils::RWLock!");
|
||||
pthread_rwlockattr_destroy(&attr);
|
||||
// TODO(gitbuda): Implement RWLock::RWLock(Priority)
|
||||
}
|
||||
|
||||
RWLock(const RWLock &) = delete;
|
||||
RWLock &operator=(const RWLock &) = delete;
|
||||
RWLock(RWLock &&) = delete;
|
||||
RWLock &operator=(RWLock &&) = delete;
|
||||
~RWLock() = default;
|
||||
|
||||
~RWLock() { pthread_rwlock_destroy(&lock_); }
|
||||
|
||||
void lock() { MG_ASSERT(pthread_rwlock_wrlock(&lock_) == 0, "Couldn't lock utils::RWLock!"); }
|
||||
void lock() {
|
||||
// TODO(gitbuda): Implement RWLock::lock
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
int err = pthread_rwlock_trywrlock(&lock_);
|
||||
if (err == 0) return true;
|
||||
MG_ASSERT(err == EBUSY, "Couldn't try lock utils::RWLock!");
|
||||
// TODO(gitbuda): Implement RWLock::try_lock
|
||||
return false;
|
||||
}
|
||||
|
||||
void unlock() { MG_ASSERT(pthread_rwlock_unlock(&lock_) == 0, "Couldn't unlock utils::RWLock!"); }
|
||||
void unlock() {
|
||||
// TODO(gitbuda): Implement RWLock::unlock
|
||||
}
|
||||
|
||||
void lock_shared() {
|
||||
int err;
|
||||
while (true) {
|
||||
err = pthread_rwlock_rdlock(&lock_);
|
||||
if (err == 0) {
|
||||
return;
|
||||
} else if (err == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
LOG_FATAL("Couldn't lock shared utils::RWLock!");
|
||||
}
|
||||
}
|
||||
// TODO(gitbuda): Implement RWLock::lock_shared
|
||||
}
|
||||
|
||||
bool try_lock_shared() {
|
||||
int err;
|
||||
while (true) {
|
||||
err = pthread_rwlock_tryrdlock(&lock_);
|
||||
if (err == 0) {
|
||||
return true;
|
||||
} else if (err == EBUSY) {
|
||||
// TODO(gitbuda): Implement RWLock::try_lock_shared
|
||||
return false;
|
||||
} else if (err == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
LOG_FATAL("Couldn't try lock shared utils::RWLock!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared() { MG_ASSERT(pthread_rwlock_unlock(&lock_) == 0, "Couldn't unlock shared utils::RWLock!"); }
|
||||
|
||||
private:
|
||||
pthread_rwlock_t lock_ = PTHREAD_RWLOCK_INITIALIZER;
|
||||
void unlock_shared() {
|
||||
// TODO(gitbuda): Implement RWLock::try_lock_shared
|
||||
}
|
||||
};
|
||||
|
||||
class WritePrioritizedRWLock final : public RWLock {
|
||||
|
@ -11,68 +11,30 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
/// This class is a wrapper around the `pthread_spinlock_t`. It provides a
|
||||
/// generic spin lock. The lock should be used in cases where you know that the
|
||||
/// lock will be contended only for short periods of time. This lock doesn't
|
||||
/// make any kernel calls (like sleep, or context switching) during its wait for
|
||||
/// the lock to be acquired. This property is only useful when the lock will be
|
||||
/// held for short periods of time and you don't want to introduce the extra
|
||||
/// delays of a sleep or context switch. On the assembly level
|
||||
/// `pthread_spinlock_t` is optimized to use less power, reduce branch
|
||||
/// mispredictions, etc... The explanation can be seen here:
|
||||
/// https://stackoverflow.com/questions/26583433/c11-implementation-of-spinlock-using-atomic/29195378#29195378
|
||||
/// https://software.intel.com/en-us/node/524249
|
||||
class SpinLock {
|
||||
public:
|
||||
SpinLock() {
|
||||
// `pthread_spin_init` returns -1 only when there isn't enough memory to
|
||||
// initialize the lock. That should never occur because the
|
||||
// `pthread_spinlock_t` is an `int` and memory isn't allocated by this init.
|
||||
// The message is probably here to suit all other platforms...
|
||||
MG_ASSERT(pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
}
|
||||
|
||||
SpinLock(SpinLock &&other) noexcept : lock_(other.lock_) {
|
||||
MG_ASSERT(pthread_spin_init(&other.lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
}
|
||||
|
||||
SpinLock &operator=(SpinLock &&other) noexcept {
|
||||
MG_ASSERT(pthread_spin_destroy(&lock_) == 0, "Couldn't destruct utils::SpinLock!");
|
||||
lock_ = other.lock_;
|
||||
MG_ASSERT(pthread_spin_init(&other.lock_, PTHREAD_PROCESS_PRIVATE) == 0, "Couldn't construct utils::SpinLock!");
|
||||
return *this;
|
||||
}
|
||||
SpinLock() {}
|
||||
|
||||
SpinLock(SpinLock &&other) = default;
|
||||
SpinLock &operator=(SpinLock &&other) = default;
|
||||
SpinLock(const SpinLock &) = delete;
|
||||
SpinLock &operator=(const SpinLock &) = delete;
|
||||
|
||||
~SpinLock() { MG_ASSERT(pthread_spin_destroy(&lock_) == 0, "Couldn't destruct utils::SpinLock!"); }
|
||||
~SpinLock() = default;
|
||||
|
||||
void lock() {
|
||||
// `pthread_spin_lock` returns -1 only when there is a deadlock detected
|
||||
// (errno EDEADLOCK).
|
||||
MG_ASSERT(pthread_spin_lock(&lock_) == 0, "Couldn't lock utils::SpinLock!");
|
||||
// TODO(gitbuda): Implement SpinLock::lock
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
// `pthread_spin_trylock` returns -1 only when the lock is already locked
|
||||
// (errno EBUSY).
|
||||
return pthread_spin_trylock(&lock_) == 0;
|
||||
// TODO(gitbuda): Implement SpinLock::try_lock
|
||||
return false;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// `pthread_spin_unlock` has no documented error codes that it could return,
|
||||
// so any error is a fatal error.
|
||||
MG_ASSERT(pthread_spin_unlock(&lock_) == 0, "Couldn't unlock utils::SpinLock!");
|
||||
// TODO(gitbuda): Implement SpinLock::unlock
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_spinlock_t lock_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::utils
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <execinfo.h>
|
||||
#include <fmt/format.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
|
||||
|
@ -37,10 +37,11 @@ std::optional<T> ParseNumber(const std::string_view string, const size_t size) {
|
||||
}
|
||||
|
||||
T value{};
|
||||
if (const auto [p, ec] = std::from_chars(string.data(), string.data() + size, value);
|
||||
ec != std::errc() || p != string.data() + size) {
|
||||
return std::nullopt;
|
||||
}
|
||||
// TODO(gitbuda): Figure out what to do with deleted std::from_chars function
|
||||
// if (const auto [p, ec] = std::from_chars(string.data(), string.data() + size, value);
|
||||
// ec != std::errc() || p != string.data() + size) {
|
||||
// return std::nullopt;
|
||||
// }
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -11,19 +11,10 @@
|
||||
|
||||
#include "utils/thread.hpp"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
void ThreadSetName(const std::string &name) {
|
||||
static constexpr auto max_name_length = GetMaxThreadNameSize();
|
||||
MG_ASSERT(name.size() <= max_name_length, "Thread name '{}' is too long", max_name_length);
|
||||
|
||||
if (prctl(PR_SET_NAME, name.c_str()) != 0) {
|
||||
spdlog::warn("Couldn't set thread name: {}!", name);
|
||||
}
|
||||
// TODO(gitbuda): Implement cross platform ThreadSetName func
|
||||
}
|
||||
|
||||
} // namespace memgraph::utils
|
||||
|
29
src/utils/thread/linux_thread.cpp
Normal file
29
src/utils/thread/linux_thread.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "utils/thread.hpp"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "utils/logging.hpp"
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
void ThreadSetName(const std::string &name) {
|
||||
static constexpr auto max_name_length = GetMaxThreadNameSize();
|
||||
MG_ASSERT(name.size() <= max_name_length, "Thread name '{}' is too long", max_name_length);
|
||||
|
||||
if (prctl(PR_SET_NAME, name.c_str()) != 0) {
|
||||
spdlog::warn("Couldn't set thread name: {}!", name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::utils
|
25
src/utils/thread/linux_thread.hpp
Normal file
25
src/utils/thread/linux_thread.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
/// @file
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace memgraph::utils {
|
||||
|
||||
constexpr size_t GetMaxThreadNameSize() { return 16; }
|
||||
|
||||
/// This function sets the thread name of the calling thread.
|
||||
/// Beware, the name length limit is 16 characters!
|
||||
void ThreadSetName(const std::string &name);
|
||||
|
||||
}; // namespace memgraph::utils
|
@ -9,7 +9,7 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "utils/async_timer.hpp"
|
||||
#include "utils/timer/linux_async_timer.hpp"
|
||||
|
||||
#include <csignal>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <memory>
|
||||
|
Loading…
Reference in New Issue
Block a user