diff --git a/src/utils/timer/async_timer.hpp b/src/utils/async_timer.cpp similarity index 63% rename from src/utils/timer/async_timer.hpp rename to src/utils/async_timer.cpp index 7c4e57a1a..1b3f5222a 100644 --- a/src/utils/timer/async_timer.hpp +++ b/src/utils/async_timer.cpp @@ -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 diff --git a/src/utils/async_timer.hpp b/src/utils/async_timer.hpp new file mode 100644 index 000000000..a5d3d5004 --- /dev/null +++ b/src/utils/async_timer.hpp @@ -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; +}; + +} diff --git a/src/utils/csv_parsing.cpp b/src/utils/csv_parsing.cpp index fc63bf9a4..2cdebefcd 100644 --- a/src/utils/csv_parsing.cpp +++ b/src/utils/csv_parsing.cpp @@ -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(); diff --git a/src/utils/file.cpp b/src/utils/file.cpp index 96fa0763e..6f6b3f40f 100644 --- a/src/utils/file.cpp +++ b/src/utils/file.cpp @@ -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 must fit into ssize_t!"); +// TODO(gitbuda): Port static_assert(off_t = ssize_t) +// static_assert(std::is_same_v, "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; diff --git a/src/utils/file_locker.cpp b/src/utils/file_locker.cpp index 7e8b6f6f8..1fa1b33a9 100644 --- a/src/utils/file_locker.cpp +++ b/src/utils/file_locker.cpp @@ -10,14 +10,17 @@ // licenses/APL.txt. #include "utils/file_locker.hpp" + #include +#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; } diff --git a/src/utils/file_locker.hpp b/src/utils/file_locker.hpp index 1134bd73e..9da63c072 100644 --- a/src/utils/file_locker.hpp +++ b/src/utils/file_locker.hpp @@ -10,6 +10,7 @@ // licenses/APL.txt. #pragma once + #include #include #include diff --git a/src/utils/lock/linux_rw_lock.hpp b/src/utils/lock/linux_rw_lock.hpp new file mode 100644 index 000000000..997486d65 --- /dev/null +++ b/src/utils/lock/linux_rw_lock.hpp @@ -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 +#include + +#include + +#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 diff --git a/src/utils/lock/linux_spin_lock.hpp b/src/utils/lock/linux_spin_lock.hpp new file mode 100644 index 000000000..e4665e975 --- /dev/null +++ b/src/utils/lock/linux_spin_lock.hpp @@ -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 + +#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 diff --git a/src/utils/math.hpp b/src/utils/math.hpp index eb6e645ab..2155f88ba 100644 --- a/src/utils/math.hpp +++ b/src/utils/math.hpp @@ -18,7 +18,7 @@ namespace memgraph::utils { -static_assert(std::is_same_v, +static_assert(std::is_same_v, "utils::Log requires uint64_t to be implemented as unsigned long."); /// This function computes the log2 function on integer types. It is faster than diff --git a/src/utils/memory.cpp b/src/utils/memory.cpp index 6f15183c0..65c61e546 100644 --- a/src/utils/memory.cpp +++ b/src/utils/memory.cpp @@ -87,7 +87,8 @@ void MonotonicBufferResource::Release() { void *MonotonicBufferResource::DoAllocate(size_t bytes, size_t alignment) { static_assert(std::is_same_v); - static_assert(std::is_same_v); + // TODO(gitbuda): Fix huge static_assert that size_t == uint64_t + //static_assert(std::is_same_v); 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; diff --git a/src/utils/rw_lock.hpp b/src/utils/rw_lock.hpp index beae536bf..6c84ed43b 100644 --- a/src/utils/rw_lock.hpp +++ b/src/utils/rw_lock.hpp @@ -12,113 +12,47 @@ /// @file #pragma once -#include -#include - -#include - -#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) { - return false; - } else if (err == EAGAIN) { - continue; - } else { - LOG_FATAL("Couldn't try lock shared utils::RWLock!"); - } - } + // TODO(gitbuda): Implement RWLock::try_lock_shared + return false; } - 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 { diff --git a/src/utils/spin_lock.hpp b/src/utils/spin_lock.hpp index d68cb87b5..32ad1a161 100644 --- a/src/utils/spin_lock.hpp +++ b/src/utils/spin_lock.hpp @@ -11,68 +11,30 @@ #pragma once -#include - -#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 diff --git a/src/utils/stacktrace.hpp b/src/utils/stacktrace.hpp index a2aa5a6c3..ac9313628 100644 --- a/src/utils/stacktrace.hpp +++ b/src/utils/stacktrace.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "utils/on_scope_exit.hpp" diff --git a/src/utils/temporal.cpp b/src/utils/temporal.cpp index a84a26b0a..cd22e7fa4 100644 --- a/src/utils/temporal.cpp +++ b/src/utils/temporal.cpp @@ -37,10 +37,11 @@ std::optional 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; } diff --git a/src/utils/thread.cpp b/src/utils/thread.cpp index 943926d18..d8bc2327b 100644 --- a/src/utils/thread.cpp +++ b/src/utils/thread.cpp @@ -11,19 +11,10 @@ #include "utils/thread.hpp" -#include - -#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 diff --git a/src/utils/thread/linux_thread.cpp b/src/utils/thread/linux_thread.cpp new file mode 100644 index 000000000..943926d18 --- /dev/null +++ b/src/utils/thread/linux_thread.cpp @@ -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 + +#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 diff --git a/src/utils/thread/linux_thread.hpp b/src/utils/thread/linux_thread.hpp new file mode 100644 index 000000000..ed8725d19 --- /dev/null +++ b/src/utils/thread/linux_thread.hpp @@ -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 + +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 diff --git a/src/utils/timer/linux_async_timer.cpp b/src/utils/timer/linux_async_timer.cpp index dd5789172..e8a61d78d 100644 --- a/src/utils/timer/linux_async_timer.cpp +++ b/src/utils/timer/linux_async_timer.cpp @@ -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 diff --git a/src/utils/timer/linux_async_timer.hpp b/src/utils/timer/linux_async_timer.hpp index 53f6892a4..87ccdb50b 100644 --- a/src/utils/timer/linux_async_timer.hpp +++ b/src/utils/timer/linux_async_timer.hpp @@ -10,6 +10,7 @@ // licenses/APL.txt. #pragma once + #include #include