update tdutils from another project
GitOrigin-RevId: 79b8eb2ba229d733f82dbb17b8bd7f27471c9472
This commit is contained in:
parent
e8ac30cbbf
commit
fd6423dedf
@ -340,7 +340,7 @@ void StorageManager::schedule_next_gc() {
|
||||
if (next_gc_at > sys_time + GC_EACH) {
|
||||
next_gc_at = sys_time + GC_EACH;
|
||||
}
|
||||
next_gc_at += Random::fast(GC_DELAY, GC_DELAY + GC_RAND_DELAY);
|
||||
next_gc_at += Random::fast(static_cast<int>(GC_DELAY), static_cast<int>(GC_DELAY + GC_RAND_DELAY));
|
||||
CHECK(next_gc_at >= sys_time);
|
||||
auto next_gc_in = next_gc_at - sys_time;
|
||||
|
||||
|
@ -102,7 +102,7 @@ struct FsFileInfo {
|
||||
template <class CallbackT>
|
||||
void scan_fs(CancellationToken &token, CallbackT &&callback) {
|
||||
for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
|
||||
int32 main_file_type = static_cast<size_t>(get_main_file_type(static_cast<FileType>(i)));
|
||||
int32 main_file_type = static_cast<int32>(get_main_file_type(static_cast<FileType>(i)));
|
||||
if (i != main_file_type) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1,5 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||
|
||||
option(TDUTILS_MIME_TYPE "Generate mime types conversion (gperf is required)" ON)
|
||||
|
||||
if (WIN32)
|
||||
if (WINGETOPT_FOUND)
|
||||
set(TD_HAVE_GETOPT 1)
|
||||
endif()
|
||||
else()
|
||||
set(TD_HAVE_GETOPT 1)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(CMAKE_INSTALL_LIBDIR "lib")
|
||||
endif()
|
||||
@ -59,6 +69,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/port/Stat.cpp
|
||||
td/utils/port/StdStreams.cpp
|
||||
td/utils/port/thread_local.cpp
|
||||
td/utils/port/user.cpp
|
||||
td/utils/port/UdpSocketFd.cpp
|
||||
td/utils/port/uname.cpp
|
||||
td/utils/port/user.cpp
|
||||
@ -95,7 +106,6 @@ set(TDUTILS_SOURCE
|
||||
td/utils/JsonBuilder.cpp
|
||||
td/utils/logging.cpp
|
||||
td/utils/misc.cpp
|
||||
td/utils/MimeType.cpp
|
||||
td/utils/MpmcQueue.cpp
|
||||
td/utils/OptionParser.cpp
|
||||
td/utils/PathView.cpp
|
||||
@ -105,9 +115,10 @@ set(TDUTILS_SOURCE
|
||||
td/utils/StackAllocator.cpp
|
||||
td/utils/Status.cpp
|
||||
td/utils/StringBuilder.cpp
|
||||
td/utils/tests.cpp
|
||||
td/utils/Time.cpp
|
||||
td/utils/Timer.cpp
|
||||
td/utils/TsFileLog.cpp
|
||||
td/utils/tests.cpp
|
||||
td/utils/tl_parsers.cpp
|
||||
td/utils/translit.cpp
|
||||
td/utils/TsFileLog.cpp
|
||||
@ -139,6 +150,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/port/StdStreams.h
|
||||
td/utils/port/thread.h
|
||||
td/utils/port/thread_local.h
|
||||
td/utils/port/user.h
|
||||
td/utils/port/UdpSocketFd.h
|
||||
td/utils/port/uname.h
|
||||
td/utils/port/user.h
|
||||
@ -205,7 +217,6 @@ set(TDUTILS_SOURCE
|
||||
td/utils/List.h
|
||||
td/utils/logging.h
|
||||
td/utils/MemoryLog.h
|
||||
td/utils/MimeType.h
|
||||
td/utils/misc.h
|
||||
td/utils/MovableValue.h
|
||||
td/utils/MpmcQueue.h
|
||||
@ -232,6 +243,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/SpinLock.h
|
||||
td/utils/StackAllocator.h
|
||||
td/utils/Status.h
|
||||
td/utils/StealingQueue.h
|
||||
td/utils/Storer.h
|
||||
td/utils/StorerBase.h
|
||||
td/utils/StringBuilder.h
|
||||
@ -241,6 +253,7 @@ set(TDUTILS_SOURCE
|
||||
td/utils/Time.h
|
||||
td/utils/TimedStat.h
|
||||
td/utils/Timer.h
|
||||
td/utils/TsFileLog.h
|
||||
td/utils/tl_helpers.h
|
||||
td/utils/tl_parsers.h
|
||||
td/utils/tl_storers.h
|
||||
@ -257,12 +270,19 @@ set(TDUTILS_SOURCE
|
||||
td/utils/VectorQueue.h
|
||||
)
|
||||
|
||||
if (TDUTILS_MIME_TYPE)
|
||||
set(TDUTILS_SOURCE
|
||||
${TDUTILS_SOURCE}
|
||||
td/utils/MimeType.cpp
|
||||
td/utils/MimeType.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(TDUTILS_TEST_SOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/buffer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/ConcurrentHashMap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/crypto.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/Enumerator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/EpochBasedMemoryReclamation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/filesystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/gzip.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/HazardPointers.cpp
|
||||
@ -280,6 +300,7 @@ set(TDUTILS_TEST_SOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/StealingQueue.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@ -293,7 +314,7 @@ if (WIN32)
|
||||
# target_link_libraries(tdutils PRIVATE ${WS2_32_LIBRARY} ${MSWSOCK_LIBRARY})
|
||||
target_link_libraries(tdutils PRIVATE ws2_32 Mswsock Normaliz psapi)
|
||||
endif()
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if (NOT CMAKE_CROSSCOMPILING AND TDUTILS_MIME_TYPE)
|
||||
add_dependencies(tdutils tdmime_auto)
|
||||
endif()
|
||||
|
||||
@ -316,7 +337,11 @@ if (CRC32C_FOUND)
|
||||
target_link_libraries(tdutils PRIVATE crc32c)
|
||||
endif()
|
||||
if (ABSL_FOUND)
|
||||
target_link_libraries(tdutils PUBLIC absl::flat_hash_map absl::flat_hash_set absl::hash)
|
||||
target_link_libraries_system(tdutils absl::flat_hash_map absl::flat_hash_set absl::hash)
|
||||
endif()
|
||||
|
||||
if (WIN32 AND WINGETOPT_FOUND)
|
||||
target_link_libraries(tdutils PRIVATE wingetopt)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
|
@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||
# Generates files for MIME type <-> extension conversions
|
||||
# DEPENDS ON: gperf grep bash/powershell
|
||||
|
||||
if (NOT TDUTILS_MIME_TYPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY auto)
|
||||
|
||||
set(TDMIME_SOURCE
|
||||
|
@ -88,7 +88,9 @@ BigNum BigNum::from_binary(Slice str) {
|
||||
}
|
||||
|
||||
BigNum BigNum::from_le_binary(Slice str) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
return BigNum(make_unique<Impl>(BN_le2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
|
||||
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
return BigNum(make_unique<Impl>(BN_lebin2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
|
||||
#else
|
||||
string str_copy = str.str();
|
||||
@ -204,7 +206,7 @@ string BigNum::to_binary(int exact_size) const {
|
||||
}
|
||||
|
||||
string BigNum::to_le_binary(int exact_size) const {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || defined(OPENSSL_IS_BORINGSSL)
|
||||
int num_size = get_num_bytes();
|
||||
if (exact_size == -1) {
|
||||
exact_size = num_size;
|
||||
@ -212,7 +214,11 @@ string BigNum::to_le_binary(int exact_size) const {
|
||||
CHECK(exact_size >= num_size);
|
||||
}
|
||||
string res(exact_size, '\0');
|
||||
#if defined(OPENSSL_IS_BORINGSSL)
|
||||
BN_bn2le_padded(MutableSlice(res).ubegin(), exact_size, impl_->big_num);
|
||||
#else
|
||||
BN_bn2lebinpad(impl_->big_num, MutableSlice(res).ubegin(), exact_size);
|
||||
#endif
|
||||
return res;
|
||||
#else
|
||||
string result = to_binary(exact_size);
|
||||
|
@ -72,6 +72,12 @@ class BufferedFd : public BufferedFdBase<FdT> {
|
||||
~BufferedFd();
|
||||
|
||||
void close();
|
||||
size_t left_unread() {
|
||||
return input_reader_.size();
|
||||
}
|
||||
size_t left_unwritten() {
|
||||
return output_reader_.size();
|
||||
}
|
||||
|
||||
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
|
||||
Result<size_t> flush_write() TD_WARN_UNUSED_RESULT;
|
||||
|
@ -20,8 +20,13 @@ struct RawCancellationToken {
|
||||
class CancellationToken {
|
||||
public:
|
||||
explicit operator bool() const {
|
||||
// Empty CancellationToken is never cancelled
|
||||
if (!token_) {
|
||||
return false;
|
||||
}
|
||||
return token_->is_cancelled_.load(std::memory_order_acquire);
|
||||
}
|
||||
CancellationToken() = default;
|
||||
explicit CancellationToken(std::shared_ptr<detail::RawCancellationToken> token) : token_(std::move(token)) {
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ void FileLog::append(CSlice cslice, int log_level) {
|
||||
process_fatal_error(cslice);
|
||||
}
|
||||
|
||||
if (size_ > rotate_threshold_) {
|
||||
if (size_ > rotate_threshold_ || want_rotate_.load(std::memory_order_relaxed)) {
|
||||
auto status = rename(path_, PSLICE() << path_ << ".old");
|
||||
if (status.is_error()) {
|
||||
process_fatal_error(PSLICE() << status.error() << " in " << __FILE__ << " at " << __LINE__);
|
||||
@ -98,9 +98,13 @@ void FileLog::rotate() {
|
||||
do_rotate();
|
||||
}
|
||||
|
||||
void FileLog::lazy_rotate() {
|
||||
want_rotate_ = true;
|
||||
}
|
||||
|
||||
void FileLog::do_rotate() {
|
||||
auto current_verbosity_level = GET_VERBOSITY_LEVEL();
|
||||
SET_VERBOSITY_LEVEL(std::numeric_limits<int>::min()); // to ensure that nothing will be printed to the closed log
|
||||
want_rotate_ = false;
|
||||
td::ScopedDisableLog disable_log; // to ensure that nothing will be printed to the closed log
|
||||
CHECK(!path_.empty());
|
||||
fd_.close();
|
||||
auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write);
|
||||
@ -112,7 +116,12 @@ void FileLog::do_rotate() {
|
||||
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
|
||||
}
|
||||
size_ = 0;
|
||||
SET_VERBOSITY_LEVEL(current_verbosity_level);
|
||||
}
|
||||
|
||||
Result<td::unique_ptr<LogInterface>> FileLog::create(string path, int64 rotate_threshold, bool redirect_stderr) {
|
||||
auto l = make_unique<FileLog>();
|
||||
TRY_STATUS(l->init(std::move(path), rotate_threshold, redirect_stderr));
|
||||
return std::move(l);
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -18,6 +18,8 @@ class FileLog : public LogInterface {
|
||||
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
|
||||
|
||||
public:
|
||||
static Result<td::unique_ptr<LogInterface>> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD,
|
||||
bool redirect_stderr = true);
|
||||
Status init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD, bool redirect_stderr = true);
|
||||
|
||||
Slice get_path() const;
|
||||
@ -32,12 +34,15 @@ class FileLog : public LogInterface {
|
||||
|
||||
void rotate() override;
|
||||
|
||||
void lazy_rotate();
|
||||
|
||||
private:
|
||||
FileFd fd_;
|
||||
string path_;
|
||||
int64 size_ = 0;
|
||||
int64 rotate_threshold_ = 0;
|
||||
bool redirect_stderr_ = false;
|
||||
std::atomic<bool> want_rotate_{};
|
||||
|
||||
void do_rotate();
|
||||
};
|
||||
|
@ -37,6 +37,10 @@ class KHeap {
|
||||
return array_[0].key_;
|
||||
}
|
||||
|
||||
HeapNode *top() const {
|
||||
return array_[0].node_;
|
||||
}
|
||||
|
||||
HeapNode *pop() {
|
||||
CHECK(!empty());
|
||||
HeapNode *result = array_[0].node_;
|
||||
|
@ -172,7 +172,7 @@ class JsonObjectScope;
|
||||
|
||||
class JsonBuilder {
|
||||
public:
|
||||
explicit JsonBuilder(StringBuilder &&sb, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
|
||||
explicit JsonBuilder(StringBuilder &&sb = {}, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
|
||||
}
|
||||
StringBuilder &string_builder() {
|
||||
return sb_;
|
||||
@ -350,7 +350,10 @@ class JsonArrayScope : public JsonScope {
|
||||
}
|
||||
void leave() {
|
||||
jb_->dec_offset();
|
||||
jb_->print_offset();
|
||||
if (jb_->is_pretty()) {
|
||||
*sb_ << "\n";
|
||||
jb_->print_offset();
|
||||
}
|
||||
*sb_ << "]";
|
||||
}
|
||||
template <class T>
|
||||
@ -369,7 +372,10 @@ class JsonArrayScope : public JsonScope {
|
||||
} else {
|
||||
is_first_ = true;
|
||||
}
|
||||
jb_->print_offset();
|
||||
if (jb_->is_pretty()) {
|
||||
*sb_ << "\n";
|
||||
jb_->print_offset();
|
||||
}
|
||||
return jb_->enter_value();
|
||||
}
|
||||
|
||||
@ -391,7 +397,10 @@ class JsonObjectScope : public JsonScope {
|
||||
}
|
||||
void leave() {
|
||||
jb_->dec_offset();
|
||||
jb_->print_offset();
|
||||
if (jb_->is_pretty()) {
|
||||
*sb_ << "\n";
|
||||
jb_->print_offset();
|
||||
}
|
||||
*sb_ << "}";
|
||||
}
|
||||
template <class T>
|
||||
@ -402,7 +411,10 @@ class JsonObjectScope : public JsonScope {
|
||||
} else {
|
||||
is_first_ = true;
|
||||
}
|
||||
jb_->print_offset();
|
||||
if (jb_->is_pretty()) {
|
||||
*sb_ << "\n";
|
||||
jb_->print_offset();
|
||||
}
|
||||
jb_->enter_value() << key;
|
||||
if (jb_->is_pretty()) {
|
||||
*sb_ << " : ";
|
||||
|
@ -105,24 +105,28 @@ template <class T>
|
||||
class OneValue<T *> {
|
||||
public:
|
||||
bool set_value(T *value) {
|
||||
T *was = nullptr;
|
||||
T *was = Empty();
|
||||
return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel);
|
||||
}
|
||||
bool get_value(T *&value) {
|
||||
value = state_.exchange(Taken(), std::memory_order_acq_rel);
|
||||
return value != nullptr;
|
||||
return value != Empty();
|
||||
}
|
||||
void reset() {
|
||||
state_ = nullptr;
|
||||
state_ = Empty();
|
||||
}
|
||||
OneValue() {
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<T *> state_{nullptr};
|
||||
T *Taken() {
|
||||
static T xxx;
|
||||
return &xxx;
|
||||
std::atomic<T *> state_{Empty()};
|
||||
static T *Empty() {
|
||||
static int64 xxx;
|
||||
return reinterpret_cast<T *>(&xxx);
|
||||
}
|
||||
static T *Taken() {
|
||||
static int64 xxx;
|
||||
return reinterpret_cast<T *>(&xxx);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,58 +7,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
class MpmcWaiter {
|
||||
class MpmcEagerWaiter {
|
||||
public:
|
||||
int wait(int yields, uint32 worker_id) {
|
||||
if (yields < RoundsTillSleepy) {
|
||||
struct Slot {
|
||||
private:
|
||||
friend class MpmcEagerWaiter;
|
||||
int yields;
|
||||
uint32 worker_id;
|
||||
};
|
||||
void init_slot(Slot &slot, uint32 worker_id) {
|
||||
slot.yields = 0;
|
||||
slot.worker_id = worker_id;
|
||||
}
|
||||
void wait(Slot &slot) {
|
||||
if (slot.yields < RoundsTillSleepy) {
|
||||
td::this_thread::yield();
|
||||
return yields + 1;
|
||||
} else if (yields == RoundsTillSleepy) {
|
||||
slot.yields++;
|
||||
return;
|
||||
} else if (slot.yields == RoundsTillSleepy) {
|
||||
auto state = state_.load(std::memory_order_relaxed);
|
||||
if (!State::has_worker(state)) {
|
||||
auto new_state = State::with_worker(state, worker_id);
|
||||
auto new_state = State::with_worker(state, slot.worker_id);
|
||||
if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
|
||||
td::this_thread::yield();
|
||||
return yields + 1;
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
if (state == State::awake()) {
|
||||
return 0;
|
||||
slot.yields = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
td::this_thread::yield();
|
||||
return 0;
|
||||
} else if (yields < RoundsTillAsleep) {
|
||||
slot.yields = 0;
|
||||
return;
|
||||
} else if (slot.yields < RoundsTillAsleep) {
|
||||
auto state = state_.load(std::memory_order_acquire);
|
||||
if (State::still_sleepy(state, worker_id)) {
|
||||
if (State::still_sleepy(state, slot.worker_id)) {
|
||||
td::this_thread::yield();
|
||||
return yields + 1;
|
||||
slot.yields++;
|
||||
return;
|
||||
}
|
||||
return 0;
|
||||
slot.yields = 0;
|
||||
return;
|
||||
} else {
|
||||
auto state = state_.load(std::memory_order_acquire);
|
||||
if (State::still_sleepy(state, worker_id)) {
|
||||
if (State::still_sleepy(state, slot.worker_id)) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) {
|
||||
condition_variable_.wait(lock);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
slot.yields = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int stop_wait(int yields, uint32 worker_id) {
|
||||
if (yields > RoundsTillSleepy) {
|
||||
void stop_wait(Slot &slot) {
|
||||
if (slot.yields > RoundsTillSleepy) {
|
||||
notify_cold();
|
||||
}
|
||||
return 0;
|
||||
slot.yields = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void close() {
|
||||
}
|
||||
|
||||
void notify() {
|
||||
@ -90,8 +113,8 @@ class MpmcWaiter {
|
||||
return (state >> 1) == (worker + 1);
|
||||
}
|
||||
};
|
||||
//enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
|
||||
enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
|
||||
enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
|
||||
// enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
|
||||
std::atomic<uint32> state_{State::awake()};
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_variable_;
|
||||
@ -105,4 +128,208 @@ class MpmcWaiter {
|
||||
}
|
||||
};
|
||||
|
||||
class MpmcSleepyWaiter {
|
||||
public:
|
||||
struct Slot {
|
||||
private:
|
||||
friend class MpmcSleepyWaiter;
|
||||
|
||||
enum State { Search, Work, Sleep } state_{Work};
|
||||
|
||||
void park() {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
condition_variable_.wait(guard, [&] { return unpark_flag_; });
|
||||
unpark_flag_ = false;
|
||||
}
|
||||
|
||||
bool cancel_park() {
|
||||
auto res = unpark_flag_;
|
||||
unpark_flag_ = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
void unpark() {
|
||||
//TODO: try unlock guard before notify_all
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
unpark_flag_ = true;
|
||||
condition_variable_.notify_all();
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_variable_;
|
||||
bool unpark_flag_{false}; // TODO: move out of lock
|
||||
int yield_cnt{0};
|
||||
int32 worker_id{0};
|
||||
char padding[TD_CONCURRENCY_PAD];
|
||||
};
|
||||
|
||||
// There are a lot of workers
|
||||
// Each has a slot
|
||||
//
|
||||
// States of a worker:
|
||||
// - searching for work | Search
|
||||
// - processing work | Work
|
||||
// - sleeping | Sleep
|
||||
//
|
||||
// When somebody adds a work it calls notify
|
||||
//
|
||||
// notify
|
||||
// if there are workers in search phase do nothing.
|
||||
// if all workers are awake do nothing
|
||||
// otherwise wake some random worker
|
||||
//
|
||||
// Initially all workers are in Search mode.
|
||||
//
|
||||
// When worker found nothing it may try to call wait.
|
||||
// This may put it in a Sleep for some time.
|
||||
// After wait return worker will be in Search state again.
|
||||
//
|
||||
// Suppose worker found a work and ready to process it.
|
||||
// Than it may call stop_wait. This will cause transition from
|
||||
// Search to Work state.
|
||||
//
|
||||
// Main invariant:
|
||||
// After notify is called there should be at least on worker in Search or Work state.
|
||||
// If possible - in Search state
|
||||
//
|
||||
|
||||
void init_slot(Slot &slot, int32 worker_id) {
|
||||
slot.state_ = Slot::State::Work;
|
||||
slot.unpark_flag_ = false;
|
||||
slot.worker_id = worker_id;
|
||||
VLOG(waiter) << "Init slot " << worker_id;
|
||||
}
|
||||
|
||||
int VERBOSITY_NAME(waiter) = VERBOSITY_NAME(DEBUG) + 10;
|
||||
void wait(Slot &slot) {
|
||||
if (slot.state_ == Slot::State::Work) {
|
||||
VLOG(waiter) << "Work -> Search";
|
||||
state_++;
|
||||
slot.state_ = Slot::State::Search;
|
||||
slot.yield_cnt = 0;
|
||||
return;
|
||||
}
|
||||
if (slot.state_ == Slot::Search) {
|
||||
if (slot.yield_cnt++ < 10 && false) {
|
||||
td::this_thread::yield();
|
||||
return;
|
||||
}
|
||||
|
||||
slot.state_ = Slot::State::Sleep;
|
||||
std::unique_lock<std::mutex> guard(sleepers_mutex_);
|
||||
auto state_view = StateView(state_.fetch_add((1 << PARKING_SHIFT) - 1));
|
||||
CHECK(state_view.searching_count != 0);
|
||||
bool should_search = state_view.searching_count == 1;
|
||||
if (closed_) {
|
||||
return;
|
||||
}
|
||||
sleepers_.push_back(&slot);
|
||||
LOG_CHECK(slot.unpark_flag_ == false) << slot.worker_id;
|
||||
VLOG(waiter) << "add to sleepers " << slot.worker_id;
|
||||
//guard.unlock();
|
||||
if (should_search) {
|
||||
VLOG(waiter) << "Search -> Search once then Sleep ";
|
||||
return;
|
||||
}
|
||||
VLOG(waiter) << "Search -> Sleep " << state_view.searching_count << " " << state_view.parked_count;
|
||||
}
|
||||
|
||||
CHECK(slot.state_ == Slot::State::Sleep);
|
||||
VLOG(waiter) << "Park " << slot.worker_id;
|
||||
slot.park();
|
||||
VLOG(waiter) << "Resume " << slot.worker_id;
|
||||
slot.state_ = Slot::State::Search;
|
||||
slot.yield_cnt = 0;
|
||||
}
|
||||
|
||||
void stop_wait(Slot &slot) {
|
||||
if (slot.state_ == Slot::State::Work) {
|
||||
return;
|
||||
}
|
||||
if (slot.state_ == Slot::State::Sleep) {
|
||||
VLOG(waiter) << "Search once then Sleep -> Work/Search " << slot.worker_id;
|
||||
slot.state_ = Slot::State::Work;
|
||||
std::unique_lock<std::mutex> guard(sleepers_mutex_);
|
||||
auto it = std::find(sleepers_.begin(), sleepers_.end(), &slot);
|
||||
if (it != sleepers_.end()) {
|
||||
sleepers_.erase(it);
|
||||
VLOG(waiter) << "remove from sleepers " << slot.worker_id;
|
||||
state_.fetch_sub((1 << PARKING_SHIFT) - 1);
|
||||
guard.unlock();
|
||||
} else {
|
||||
guard.unlock();
|
||||
VLOG(waiter) << "not in sleepers" << slot.worker_id;
|
||||
CHECK(slot.cancel_park());
|
||||
}
|
||||
}
|
||||
VLOG(waiter) << "Search once then Sleep -> Work " << slot.worker_id;
|
||||
slot.state_ = Slot::State::Search;
|
||||
auto state_view = StateView(state_.fetch_sub(1));
|
||||
CHECK(state_view.searching_count != 0);
|
||||
CHECK(state_view.searching_count < 1000);
|
||||
bool should_notify = state_view.searching_count == 1;
|
||||
if (should_notify) {
|
||||
VLOG(waiter) << "Notify others";
|
||||
notify();
|
||||
}
|
||||
VLOG(waiter) << "Search -> Work ";
|
||||
slot.state_ = Slot::State::Work;
|
||||
}
|
||||
|
||||
void notify() {
|
||||
auto view = StateView(state_.load());
|
||||
//LOG(ERROR) << view.parked_count;
|
||||
if (view.searching_count > 0 || view.parked_count == 0) {
|
||||
VLOG(waiter) << "Ingore notify: " << view.searching_count << " " << view.parked_count;
|
||||
return;
|
||||
}
|
||||
|
||||
VLOG(waiter) << "Notify: " << view.searching_count << " " << view.parked_count;
|
||||
std::unique_lock<std::mutex> guard(sleepers_mutex_);
|
||||
|
||||
view = StateView(state_.load());
|
||||
if (view.searching_count > 0) {
|
||||
VLOG(waiter) << "Skip notify: got searching";
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(view.parked_count == static_cast<int>(sleepers_.size()));
|
||||
if (sleepers_.empty()) {
|
||||
VLOG(waiter) << "Skip notify: no sleepers";
|
||||
return;
|
||||
}
|
||||
|
||||
auto sleeper = sleepers_.back();
|
||||
sleepers_.pop_back();
|
||||
state_.fetch_sub((1 << PARKING_SHIFT) - 1);
|
||||
VLOG(waiter) << "Unpark " << sleeper->worker_id;
|
||||
sleeper->unpark();
|
||||
}
|
||||
|
||||
void close() {
|
||||
StateView state(state_.load());
|
||||
LOG_CHECK(state.parked_count == 0) << state.parked_count;
|
||||
LOG_CHECK(state.searching_count == 0) << state.searching_count;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr td::int32 PARKING_SHIFT = 16;
|
||||
struct StateView {
|
||||
td::int32 parked_count;
|
||||
td::int32 searching_count;
|
||||
explicit StateView(int32 x) {
|
||||
parked_count = x >> PARKING_SHIFT;
|
||||
searching_count = x & ((1 << PARKING_SHIFT) - 1);
|
||||
}
|
||||
};
|
||||
std::atomic<td::int32> state_{0};
|
||||
|
||||
std::mutex sleepers_mutex_;
|
||||
std::vector<Slot *> sleepers_;
|
||||
|
||||
bool closed_ = false;
|
||||
};
|
||||
|
||||
using MpmcWaiter = MpmcSleepyWaiter;
|
||||
|
||||
} // namespace td
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/OptionParser.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
@ -19,6 +20,11 @@ void OptionParser::set_description(string description) {
|
||||
|
||||
void OptionParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
for (auto &option : options_) {
|
||||
if (option.short_key == short_key || (!long_key.empty() && long_key == option.long_key)) {
|
||||
LOG(ERROR) << "Ignore duplicated option '" << short_key << "' '" << long_key << "'";
|
||||
}
|
||||
}
|
||||
options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,10 @@ class OptionParser {
|
||||
|
||||
void add_checked_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback);
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback) = delete;
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback) = delete;
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<void(Slice)> callback);
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<void(void)> callback);
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/PathView.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
@ -36,4 +37,22 @@ Slice PathView::relative(Slice path, Slice dir, bool force) {
|
||||
return path;
|
||||
}
|
||||
|
||||
Slice PathView::dir_and_file(Slice path) {
|
||||
auto last_slash = static_cast<int32>(path.size()) - 1;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
last_slash--;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
return path.substr(last_slash + 1);
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -28,6 +28,9 @@ class PathView {
|
||||
Slice parent_dir() const {
|
||||
return path_.substr(0, last_slash_ + 1);
|
||||
}
|
||||
Slice parent_dir_noslash() const {
|
||||
return last_slash_ <= 0 ? td::Slice(".") : path_.substr(0, last_slash_);
|
||||
}
|
||||
|
||||
Slice extension() const {
|
||||
if (last_dot_ == static_cast<int32>(path_.size())) {
|
||||
@ -61,6 +64,7 @@ class PathView {
|
||||
}
|
||||
|
||||
static Slice relative(Slice path, Slice dir, bool force = false);
|
||||
static Slice dir_and_file(Slice path);
|
||||
|
||||
private:
|
||||
static bool is_slash(char c) {
|
||||
|
@ -39,6 +39,11 @@ void Random::secure_bytes(unsigned char *ptr, size_t size) {
|
||||
buf_pos = BUF_SIZE;
|
||||
generation = 0;
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
td::MutableSlice(buf, BUF_SIZE).fill_zero_secure();
|
||||
buf_pos = BUF_SIZE;
|
||||
return;
|
||||
}
|
||||
if (generation != random_seed_generation.load(std::memory_order_relaxed)) {
|
||||
generation = random_seed_generation.load(std::memory_order_acquire);
|
||||
buf_pos = BUF_SIZE;
|
||||
@ -97,6 +102,10 @@ void Random::add_seed(Slice bytes, double entropy) {
|
||||
RAND_add(bytes.data(), static_cast<int>(bytes.size()), entropy);
|
||||
random_seed_generation++;
|
||||
}
|
||||
|
||||
void Random::secure_cleanup() {
|
||||
Random::secure_bytes(nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int rand_device_helper() {
|
||||
@ -134,13 +143,21 @@ int Random::fast(int min, int max) {
|
||||
return static_cast<int>(min + fast_uint32() % (max - min + 1)); // TODO signed_cast
|
||||
}
|
||||
|
||||
double Random::fast(double min, double max) {
|
||||
DCHECK(min <= max);
|
||||
return min +
|
||||
fast_uint32() * 1.0 /
|
||||
(static_cast<double>(std::numeric_limits<td::uint32>::max()) - std::numeric_limits<td::uint32>::min()) *
|
||||
(max - min);
|
||||
}
|
||||
|
||||
Random::Xorshift128plus::Xorshift128plus(uint64 seed) {
|
||||
auto next = [&] {
|
||||
// splitmix64
|
||||
seed += static_cast<uint64>(0x9E3779B97F4A7C15);
|
||||
seed += static_cast<uint64>(0x9E3779B97F4A7C15ull);
|
||||
uint64 z = seed;
|
||||
z = (z ^ (z >> 30)) * static_cast<uint64>(0xBF58476D1CE4E5B9);
|
||||
z = (z ^ (z >> 27)) * static_cast<uint64>(0x94D049BB133111EB);
|
||||
z = (z ^ (z >> 30)) * static_cast<uint64>(0xBF58476D1CE4E5B9ull);
|
||||
z = (z ^ (z >> 27)) * static_cast<uint64>(0x94D049BB133111EBull);
|
||||
return z ^ (z >> 31);
|
||||
};
|
||||
seed_[0] = next();
|
||||
@ -164,6 +181,9 @@ uint64 Random::Xorshift128plus::operator()() {
|
||||
int Random::Xorshift128plus::fast(int min, int max) {
|
||||
return static_cast<int>((*this)() % (max - min + 1) + min);
|
||||
}
|
||||
int64 Random::Xorshift128plus::fast64(int64 min, int64 max) {
|
||||
return static_cast<int64>((*this)() % (max - min + 1) + min);
|
||||
}
|
||||
|
||||
void Random::Xorshift128plus::bytes(MutableSlice dest) {
|
||||
int cnt = 0;
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Span.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -23,6 +24,7 @@ class Random {
|
||||
|
||||
// works only for current thread
|
||||
static void add_seed(Slice bytes, double entropy = 0);
|
||||
static void secure_cleanup();
|
||||
#endif
|
||||
|
||||
static uint32 fast_uint32();
|
||||
@ -30,13 +32,21 @@ class Random {
|
||||
|
||||
// distribution is not uniform, min and max are included
|
||||
static int fast(int min, int max);
|
||||
static double fast(double min, double max);
|
||||
|
||||
class Fast {
|
||||
public:
|
||||
uint64 operator()() {
|
||||
return fast_uint64();
|
||||
}
|
||||
};
|
||||
class Xorshift128plus {
|
||||
public:
|
||||
explicit Xorshift128plus(uint64 seed);
|
||||
Xorshift128plus(uint64 seed_a, uint64 seed_b);
|
||||
uint64 operator()();
|
||||
int fast(int min, int max);
|
||||
int64 fast64(int64 min, int64 max);
|
||||
void bytes(MutableSlice dest);
|
||||
|
||||
private:
|
||||
@ -44,4 +54,12 @@ class Random {
|
||||
};
|
||||
};
|
||||
|
||||
template <class T, class R>
|
||||
void random_shuffle(td::MutableSpan<T> v, R &rnd) {
|
||||
for (std::size_t i = 1; i < v.size(); i++) {
|
||||
auto pos = static_cast<std::size_t>(rnd() % (i + 1));
|
||||
std::swap(v[i], v[pos]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -37,9 +37,7 @@ class AtomicRefCnt {
|
||||
};
|
||||
|
||||
template <class DataT, class DeleterT>
|
||||
class SharedPtrRaw
|
||||
: public DeleterT
|
||||
, private MpscLinkQueueImpl::Node {
|
||||
class SharedPtrRaw : public DeleterT, private MpscLinkQueueImpl::Node {
|
||||
public:
|
||||
explicit SharedPtrRaw(DeleterT deleter) : DeleterT(std::move(deleter)), ref_cnt_{0}, option_magic_(Magic) {
|
||||
}
|
||||
@ -88,6 +86,7 @@ template <class T, class DeleterT = std::default_delete<T>>
|
||||
class SharedPtr {
|
||||
public:
|
||||
using Raw = detail::SharedPtrRaw<T, DeleterT>;
|
||||
struct acquire_t {};
|
||||
SharedPtr() = default;
|
||||
~SharedPtr() {
|
||||
if (!raw_) {
|
||||
@ -100,6 +99,8 @@ class SharedPtr {
|
||||
raw_->inc();
|
||||
}
|
||||
}
|
||||
SharedPtr(acquire_t, Raw *raw) : raw_(raw) {
|
||||
}
|
||||
SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
|
||||
}
|
||||
SharedPtr &operator=(const SharedPtr &other) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/SharedSlice.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
|
@ -4,9 +4,9 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -72,6 +72,9 @@ class UnsafeSharedSlice {
|
||||
public:
|
||||
UnsafeSharedSlice() = default;
|
||||
UnsafeSharedSlice clone() const {
|
||||
if (is_null()) {
|
||||
return UnsafeSharedSlice();
|
||||
}
|
||||
header()->inc();
|
||||
return UnsafeSharedSlice(ptr_.get());
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#if TD_HAVE_OPENSSL
|
||||
|
@ -85,6 +85,16 @@ class SpanImpl {
|
||||
return data_[size() - 1];
|
||||
}
|
||||
|
||||
InnerT &front() {
|
||||
DCHECK(!empty());
|
||||
return data_[0];
|
||||
}
|
||||
|
||||
const InnerT &front() const {
|
||||
DCHECK(!empty());
|
||||
return data_[0];
|
||||
}
|
||||
|
||||
InnerT *data() const {
|
||||
return data_;
|
||||
}
|
||||
@ -95,7 +105,6 @@ class SpanImpl {
|
||||
InnerT *end() const {
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
std::reverse_iterator<InnerT *> rbegin() const {
|
||||
return std::reverse_iterator<InnerT *>(end());
|
||||
}
|
||||
@ -111,8 +120,9 @@ class SpanImpl {
|
||||
}
|
||||
|
||||
SpanImpl &truncate(size_t size) {
|
||||
CHECK(size <= size_);
|
||||
size_ = size;
|
||||
if (size < size_) {
|
||||
size_ = size;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -138,9 +148,45 @@ template <class T>
|
||||
Span<T> span(const T *ptr, size_t size) {
|
||||
return Span<T>(ptr, size);
|
||||
}
|
||||
template <class T>
|
||||
Span<T> span(const std::vector<T> &vec) {
|
||||
return Span<T>(vec);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
MutableSpan<T> mutable_span(T *ptr, size_t size) {
|
||||
return MutableSpan<T>(ptr, size);
|
||||
}
|
||||
template <class T>
|
||||
MutableSpan<T> mutable_span(std::vector<T> &vec) {
|
||||
return MutableSpan<T>(vec);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Span<T> span_one(const T &value) {
|
||||
return td::Span<T>(&value, 1);
|
||||
}
|
||||
template <class T>
|
||||
MutableSpan<T> mutable_span_one(T &value) {
|
||||
return td::MutableSpan<T>(&value, 1);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Span<T> as_span(Span<T> span) {
|
||||
return span;
|
||||
}
|
||||
template <class T>
|
||||
Span<T> as_span(const std::vector<T> &vec) {
|
||||
return Span<T>(vec);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
MutableSpan<T> as_mutable_span(MutableSpan<T> span) {
|
||||
return span;
|
||||
}
|
||||
template <class T>
|
||||
MutableSpan<T> as_mutable_span(std::vector<T> &vec) {
|
||||
return MutableSpan<T>(vec);
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -36,16 +36,46 @@
|
||||
} \
|
||||
}
|
||||
|
||||
#define TRY_STATUS_PROMISE(promise_name, status) \
|
||||
{ \
|
||||
auto try_status = (status); \
|
||||
if (try_status.is_error()) { \
|
||||
promise_name.set_error(std::move(try_status)); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TRY_STATUS_PROMISE_PREFIX(promise_name, status, prefix) \
|
||||
{ \
|
||||
auto try_status = (status); \
|
||||
if (try_status.is_error()) { \
|
||||
promise_name.set_error(try_status.move_as_error_prefix(prefix)); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
|
||||
|
||||
#define TRY_RESULT_PROMISE(promise_name, name, result) \
|
||||
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
|
||||
|
||||
#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result)
|
||||
|
||||
#define TRY_RESULT_PROMISE_ASSIGN(promise_name, name, result) \
|
||||
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
|
||||
|
||||
#define TRY_RESULT_PREFIX(name, result, prefix) \
|
||||
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
|
||||
|
||||
#define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \
|
||||
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
|
||||
|
||||
#define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \
|
||||
TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
|
||||
|
||||
#define TRY_RESULT_PROMISE_PREFIX_ASSIGN(promise_name, name, result, prefix) \
|
||||
TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
|
||||
|
||||
#define TRY_RESULT_IMPL(r_name, name, result) \
|
||||
auto r_name = (result); \
|
||||
if (r_name.is_error()) { \
|
||||
@ -60,6 +90,22 @@
|
||||
} \
|
||||
name = r_name.move_as_ok();
|
||||
|
||||
#define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \
|
||||
auto r_name = (result); \
|
||||
if (r_name.is_error()) { \
|
||||
promise_name.set_error(r_name.move_as_error()); \
|
||||
return; \
|
||||
} \
|
||||
name = r_name.move_as_ok();
|
||||
|
||||
#define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
|
||||
auto r_name = (result); \
|
||||
if (r_name.is_error()) { \
|
||||
promise_name.set_error(r_name.move_as_error_prefix(prefix)); \
|
||||
return; \
|
||||
} \
|
||||
name = r_name.move_as_ok();
|
||||
|
||||
#define LOG_STATUS(status) \
|
||||
{ \
|
||||
auto log_status = (status); \
|
||||
@ -272,7 +318,16 @@ class Status {
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
|
||||
Auto move_as_ok() {
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
|
||||
return status.move_as_error_suffix(message());
|
||||
}
|
||||
|
||||
Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT {
|
||||
CHECK(is_error());
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
@ -285,6 +340,19 @@ class Status {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT {
|
||||
CHECK(is_error());
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
case ErrorType::General:
|
||||
return Error(code(), PSLICE() << message() << suffix);
|
||||
case ErrorType::Os:
|
||||
return Status(false, ErrorType::Os, code(), PSLICE() << message() << suffix);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Info {
|
||||
@ -366,6 +434,7 @@ class Status {
|
||||
template <class T = Unit>
|
||||
class Result {
|
||||
public:
|
||||
using ValueT = T;
|
||||
Result() : status_(Status::Error<-1>()) {
|
||||
}
|
||||
template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0>
|
||||
@ -462,6 +531,18 @@ class Result {
|
||||
};
|
||||
return status_.move_as_error_prefix(prefix);
|
||||
}
|
||||
Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT {
|
||||
SCOPE_EXIT {
|
||||
status_ = Status::Error<-5>();
|
||||
};
|
||||
return status_.move_as_error_prefix(prefix);
|
||||
}
|
||||
Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT {
|
||||
SCOPE_EXIT {
|
||||
status_ = Status::Error<-5>();
|
||||
};
|
||||
return status_.move_as_error_suffix(suffix);
|
||||
}
|
||||
const T &ok() const {
|
||||
LOG_CHECK(status_.is_ok()) << status_;
|
||||
return value_;
|
||||
@ -489,6 +570,22 @@ class Result {
|
||||
*this = Result<T>();
|
||||
}
|
||||
|
||||
template <class F>
|
||||
td::Result<decltype(std::declval<F>()(std::declval<T>()))> move_map(F &&f) {
|
||||
if (is_error()) {
|
||||
return move_as_error();
|
||||
}
|
||||
return f(move_as_ok());
|
||||
}
|
||||
|
||||
template <class F>
|
||||
decltype(std::declval<F>()(std::declval<T>())) move_fmap(F &&f) {
|
||||
if (is_error()) {
|
||||
return move_as_error();
|
||||
}
|
||||
return f(move_as_ok());
|
||||
}
|
||||
|
||||
private:
|
||||
Status status_;
|
||||
union {
|
||||
|
@ -19,6 +19,8 @@ namespace td {
|
||||
class StringBuilder {
|
||||
public:
|
||||
explicit StringBuilder(MutableSlice slice, bool use_buffer = false);
|
||||
StringBuilder() : StringBuilder({}, true) {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
current_ptr_ = begin_ptr_;
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -13,7 +14,6 @@
|
||||
#include <atomic>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class T>
|
||||
class ThreadLocalStorage {
|
||||
public:
|
||||
@ -51,5 +51,4 @@ class ThreadLocalStorage {
|
||||
return nodes_[thread_id];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -16,7 +17,6 @@
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <size_t N>
|
||||
class ThreadSafeMultiCounter {
|
||||
public:
|
||||
@ -31,6 +31,13 @@ class ThreadSafeMultiCounter {
|
||||
tls_.for_each([&](auto &value) { res += value[index].load(std::memory_order_relaxed); });
|
||||
return res;
|
||||
}
|
||||
void clear() {
|
||||
tls_.for_each([&](auto &value) {
|
||||
for (auto &x : value) {
|
||||
x = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadLocalStorage<std::array<std::atomic<int64>, N>> tls_;
|
||||
@ -80,7 +87,7 @@ class NamedThreadSafeCounter {
|
||||
}
|
||||
}
|
||||
CHECK(names_.size() < N);
|
||||
names_.emplase_back(name.begin(), name.size());
|
||||
names_.emplace_back(name.begin(), name.size());
|
||||
return get_counter_ref(names_.size() - 1);
|
||||
}
|
||||
|
||||
@ -101,6 +108,11 @@ class NamedThreadSafeCounter {
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
counter_.clear();
|
||||
}
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const NamedThreadSafeCounter &counter) {
|
||||
counter.for_each([&sb](Slice name, int64 cnt) { sb << name << ": " << cnt << "\n"; });
|
||||
return sb;
|
||||
|
@ -7,14 +7,36 @@
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <atomic>
|
||||
|
||||
namespace td {
|
||||
|
||||
bool operator==(Timestamp a, Timestamp b) {
|
||||
return std::abs(a.at() - b.at()) < 1e-6;
|
||||
}
|
||||
namespace {
|
||||
std::atomic<double> time_diff;
|
||||
}
|
||||
double Time::now() {
|
||||
return now_unadjusted() + time_diff.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
double Time::now_unadjusted() {
|
||||
return Clocks::monotonic();
|
||||
}
|
||||
|
||||
void Time::jump_in_future(double at) {
|
||||
auto old_time_diff = time_diff.load();
|
||||
|
||||
while (true) {
|
||||
auto diff = at - now();
|
||||
if (diff < 0) {
|
||||
return;
|
||||
}
|
||||
if (time_diff.compare_exchange_strong(old_time_diff, old_time_diff + diff)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -28,6 +28,10 @@ class Time {
|
||||
// As an alternative we may say that now_cached is a thread local copy of now
|
||||
return now();
|
||||
}
|
||||
static double now_unadjusted();
|
||||
|
||||
// Used for testing. After jump_in_future(at) is called, now() >= at.
|
||||
static void jump_in_future(double at);
|
||||
};
|
||||
|
||||
inline void relax_timeout_at(double *timeout, double new_timeout) {
|
||||
@ -58,12 +62,15 @@ class Timestamp {
|
||||
return Timestamp{timeout - Clocks::system() + Time::now()};
|
||||
}
|
||||
|
||||
static Timestamp in(double timeout) {
|
||||
return Timestamp{Time::now_cached() + timeout};
|
||||
static Timestamp in(double timeout, td::Timestamp now = td::Timestamp::now_cached()) {
|
||||
return Timestamp{now.at() + timeout};
|
||||
}
|
||||
|
||||
bool is_in_past(td::Timestamp now) const {
|
||||
return at_ <= now.at();
|
||||
}
|
||||
bool is_in_past() const {
|
||||
return at_ <= Time::now_cached();
|
||||
return is_in_past(now_cached());
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
@ -99,6 +106,10 @@ class Timestamp {
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const Timestamp &a, const Timestamp &b) {
|
||||
return a.at() < b.at();
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
void store(const Timestamp ×tamp, StorerT &storer) {
|
||||
storer.store_binary(timestamp.at() - Time::now() + Clocks::system());
|
||||
|
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/optional.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -68,4 +69,27 @@ class TimedStat {
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Cmp>
|
||||
struct MinMaxStat {
|
||||
public:
|
||||
using Event = T;
|
||||
void on_event(Event event) {
|
||||
if (!best_ || Cmp()(event, best_.value())) {
|
||||
best_ = event;
|
||||
}
|
||||
}
|
||||
td::optional<T> get_stat() const {
|
||||
return best_.copy();
|
||||
}
|
||||
|
||||
private:
|
||||
td::optional<T> best_;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using MinStat = MinMaxStat<T, std::less<>>;
|
||||
|
||||
template <class T>
|
||||
using MaxStat = MinMaxStat<T, std::greater<>>;
|
||||
|
||||
} // namespace td
|
||||
|
@ -12,15 +12,40 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
Timer::Timer() : start_time_(Time::now()) {
|
||||
Timer::Timer(bool is_paused) : is_paused_(is_paused) {
|
||||
if (is_paused_) {
|
||||
start_time_ = 0;
|
||||
} else {
|
||||
start_time_ = Time::now();
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::pause() {
|
||||
if (is_paused_) {
|
||||
return;
|
||||
}
|
||||
elapsed_ += Time::now() - start_time_;
|
||||
is_paused_ = true;
|
||||
}
|
||||
|
||||
void Timer::resume() {
|
||||
if (!is_paused_) {
|
||||
return;
|
||||
}
|
||||
start_time_ = Time::now();
|
||||
is_paused_ = false;
|
||||
}
|
||||
|
||||
double Timer::elapsed() const {
|
||||
return Time::now() - start_time_;
|
||||
double res = elapsed_;
|
||||
if (!is_paused_) {
|
||||
res += Time::now() - start_time_;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) {
|
||||
return string_builder << "in " << Time::now() - timer.start_time_;
|
||||
return string_builder << format::as_time(timer.elapsed());
|
||||
}
|
||||
|
||||
PerfWarningTimer::PerfWarningTimer(string name, double max_duration)
|
||||
|
@ -12,14 +12,22 @@ namespace td {
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
Timer() : Timer(false) {
|
||||
}
|
||||
explicit Timer(bool is_paused);
|
||||
Timer(const Timer &other) = default;
|
||||
Timer &operator=(const Timer &other) = default;
|
||||
|
||||
double elapsed() const;
|
||||
void pause();
|
||||
void resume();
|
||||
|
||||
private:
|
||||
friend StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer);
|
||||
|
||||
double elapsed_{0};
|
||||
double start_time_;
|
||||
bool is_paused_{false};
|
||||
};
|
||||
|
||||
class PerfWarningTimer {
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/TsFileLog.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -16,14 +17,14 @@
|
||||
#include <limits>
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class TsFileLogImpl : public LogInterface {
|
||||
class TsFileLog : public LogInterface {
|
||||
public:
|
||||
Status init(string path) {
|
||||
Status init(string path, td::int64 rotate_threshold, bool redirect_stderr) {
|
||||
path_ = std::move(path);
|
||||
for (int32 i = 0; i < static_cast<int32>(logs_.size()); i++) {
|
||||
rotate_threshold_ = rotate_threshold;
|
||||
redirect_stderr_ = redirect_stderr;
|
||||
for (int i = 0; i < (int)logs_.size(); i++) {
|
||||
logs_[i].id = i;
|
||||
}
|
||||
return init_info(&logs_[0]);
|
||||
@ -47,17 +48,19 @@ class TsFileLogImpl : public LogInterface {
|
||||
private:
|
||||
struct Info {
|
||||
FileLog log;
|
||||
bool is_inited = false;
|
||||
int32 id;
|
||||
std::atomic<bool> is_inited{false};
|
||||
int id;
|
||||
};
|
||||
static constexpr int32 MAX_THREAD_ID = 128;
|
||||
static constexpr int MAX_THREAD_ID = 128;
|
||||
td::int64 rotate_threshold_;
|
||||
bool redirect_stderr_;
|
||||
std::string path_;
|
||||
std::array<Info, MAX_THREAD_ID> logs_;
|
||||
|
||||
LogInterface *get_current_logger() {
|
||||
auto *info = get_current_info();
|
||||
if (!info->is_inited) {
|
||||
init_info(info).ensure();
|
||||
if (!info->is_inited.load(std::memory_order_relaxed)) {
|
||||
CHECK(init_info(info).is_ok());
|
||||
}
|
||||
return &info->log;
|
||||
}
|
||||
@ -67,25 +70,31 @@ class TsFileLogImpl : public LogInterface {
|
||||
}
|
||||
|
||||
Status init_info(Info *info) {
|
||||
TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0));
|
||||
TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0 && redirect_stderr_));
|
||||
info->is_inited = true;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
string get_path(const Info *info) {
|
||||
string get_path(Info *info) {
|
||||
if (info->id == 0) {
|
||||
return path_;
|
||||
}
|
||||
return PSTRING() << path_ << "." << info->id;
|
||||
return PSTRING() << path_ << ".thread" << info->id << ".log";
|
||||
}
|
||||
|
||||
void rotate() override {
|
||||
for (auto &info : logs_) {
|
||||
if (info.is_inited.load(std::memory_order_consume)) {
|
||||
info.log.lazy_rotate();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
Result<unique_ptr<LogInterface>> TsFileLog::create(string path) {
|
||||
auto res = make_unique<detail::TsFileLogImpl>();
|
||||
TRY_STATUS(res->init(path));
|
||||
Result<td::unique_ptr<LogInterface>> TsFileLog::create(string path, td::int64 rotate_threshold, bool redirect_stderr) {
|
||||
auto res = td::make_unique<detail::TsFileLog>();
|
||||
TRY_STATUS(res->init(path, rotate_threshold, redirect_stderr));
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -11,10 +12,11 @@
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class TsFileLog {
|
||||
public:
|
||||
static Result<unique_ptr<LogInterface>> create(string path);
|
||||
};
|
||||
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
|
||||
|
||||
public:
|
||||
static Result<td::unique_ptr<LogInterface>> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD,
|
||||
bool redirect_stderr = true);
|
||||
};
|
||||
} // namespace td
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
@ -241,6 +241,10 @@ class Variant {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return offset_ == npos;
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
int64 align_;
|
||||
|
@ -32,6 +32,12 @@ class VectorQueue {
|
||||
read_pos_ += n;
|
||||
try_shrink();
|
||||
}
|
||||
T &front() {
|
||||
return vector_[read_pos_];
|
||||
}
|
||||
T &back() {
|
||||
return vector_.back();
|
||||
}
|
||||
const T &front() const {
|
||||
return vector_[read_pos_];
|
||||
}
|
||||
@ -47,6 +53,9 @@ class VectorQueue {
|
||||
const T *data() const {
|
||||
return vector_.data() + read_pos_;
|
||||
}
|
||||
T *data() {
|
||||
return vector_.data() + read_pos_;
|
||||
}
|
||||
Span<T> as_span() const {
|
||||
return {data(), size()};
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -236,4 +237,65 @@ string base64_filter(Slice input) {
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *const symbols32_lc = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
static const char *const symbols32_uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
string base32_encode(Slice input, bool upper_case) {
|
||||
auto *symbols32 = (upper_case ? symbols32_uc : symbols32_lc);
|
||||
string base32;
|
||||
base32.reserve((input.size() * 8 + 4) / 5);
|
||||
uint32 c = 0;
|
||||
uint32 length = 0;
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
c = (c << 8) | input.ubegin()[i];
|
||||
length += 8;
|
||||
while (length >= 5) {
|
||||
length -= 5;
|
||||
base32.push_back(symbols32[(c >> length) & 31]);
|
||||
}
|
||||
}
|
||||
if (length != 0) {
|
||||
base32.push_back(symbols32[(c << (5 - length)) & 31]);
|
||||
}
|
||||
//TODO: optional padding
|
||||
return base32;
|
||||
}
|
||||
|
||||
static unsigned char b32_char_to_value[256];
|
||||
static void init_base32_table() {
|
||||
static bool is_inited = [] {
|
||||
std::fill(std::begin(b32_char_to_value), std::end(b32_char_to_value), static_cast<unsigned char>(32));
|
||||
for (unsigned char i = 0; i < 32; i++) {
|
||||
b32_char_to_value[static_cast<size_t>(symbols32_lc[i])] = i;
|
||||
b32_char_to_value[static_cast<size_t>(symbols32_uc[i])] = i;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
Result<string> base32_decode(Slice base32) {
|
||||
init_base32_table();
|
||||
string res;
|
||||
res.reserve(base32.size() * 5 / 8);
|
||||
uint32 c = 0;
|
||||
uint32 length = 0;
|
||||
for (size_t i = 0; i < base32.size(); i++) {
|
||||
auto value = b32_char_to_value[base32.ubegin()[i]];
|
||||
if (value == 32) {
|
||||
return Status::Error("Wrong character in the string");
|
||||
}
|
||||
c = (c << 5) | value;
|
||||
length += 5;
|
||||
while (length >= 8) {
|
||||
length -= 8;
|
||||
res.push_back(td::uint8((c >> length) & 255));
|
||||
}
|
||||
}
|
||||
if ((c & ((1 << length) - 1)) != 0) {
|
||||
return Status::Error("Nonzero padding");
|
||||
}
|
||||
//TODO: check padding
|
||||
return res;
|
||||
}
|
||||
} // namespace td
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -27,5 +28,6 @@ bool is_base64_characters(Slice input);
|
||||
bool is_base64url_characters(Slice input);
|
||||
|
||||
string base64_filter(Slice input);
|
||||
|
||||
string base32_encode(Slice input, bool upper_case = false);
|
||||
Result<string> base32_decode(Slice base32);
|
||||
} // namespace td
|
||||
|
@ -136,6 +136,7 @@ inline uint64 bswap64(uint64 x) {
|
||||
}
|
||||
|
||||
inline int32 count_bits32(uint32 x) {
|
||||
// Do not use __popcnt because it will fail on some platforms.
|
||||
x -= (x >> 1) & 0x55555555;
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
@ -269,4 +270,40 @@ inline int32 count_bits64(uint64 x) {
|
||||
|
||||
#endif
|
||||
|
||||
struct BitsRange {
|
||||
td::uint64 bits{0};
|
||||
mutable td::int32 pos{-1};
|
||||
|
||||
explicit BitsRange(td::uint64 bits = 0) : bits{bits}, pos{-1} {
|
||||
}
|
||||
|
||||
BitsRange begin() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitsRange end() const {
|
||||
return BitsRange{};
|
||||
}
|
||||
|
||||
td::int32 operator*() const {
|
||||
if (pos == -1) {
|
||||
pos = td::count_trailing_zeroes64(bits);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool operator!=(const BitsRange &other) const {
|
||||
return bits != other.bits;
|
||||
}
|
||||
|
||||
BitsRange &operator++() {
|
||||
auto i = **this;
|
||||
if (i != 64) {
|
||||
bits ^= 1ull << i;
|
||||
}
|
||||
pos = -1;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
@ -606,16 +606,16 @@ void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlic
|
||||
aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
|
||||
}
|
||||
|
||||
AesCbcState::AesCbcState(Slice key256, Slice iv128) : key_(key256), iv_(iv128) {
|
||||
CHECK(key_.size() == 32);
|
||||
CHECK(iv_.size() == 16);
|
||||
AesCbcState::AesCbcState(Slice key256, Slice iv128) : raw_{td::SecureString(key256), td::SecureString(iv128)} {
|
||||
CHECK(raw_.key.size() == 32);
|
||||
CHECK(raw_.iv.size() == 16);
|
||||
}
|
||||
|
||||
void AesCbcState::encrypt(Slice from, MutableSlice to) {
|
||||
::td::aes_cbc_encrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
|
||||
::td::aes_cbc_encrypt(raw_.key.as_slice(), raw_.iv.as_mutable_slice(), from, to);
|
||||
}
|
||||
void AesCbcState::decrypt(Slice from, MutableSlice to) {
|
||||
::td::aes_cbc_decrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
|
||||
::td::aes_cbc_decrypt(raw_.key.as_slice(), raw_.iv.as_mutable_slice(), from, to);
|
||||
}
|
||||
|
||||
class AesCtrState::Impl {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
@ -94,10 +95,17 @@ class AesCbcState {
|
||||
|
||||
void encrypt(Slice from, MutableSlice to);
|
||||
void decrypt(Slice from, MutableSlice to);
|
||||
struct Raw {
|
||||
SecureString key;
|
||||
SecureString iv;
|
||||
};
|
||||
|
||||
const Raw &raw() const {
|
||||
return raw_;
|
||||
}
|
||||
|
||||
private:
|
||||
SecureString key_;
|
||||
SecureString iv_;
|
||||
Raw raw_;
|
||||
};
|
||||
|
||||
void sha1(Slice data, unsigned char output[20]);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/PathView.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/unicode.h"
|
||||
@ -32,7 +33,6 @@ template <>
|
||||
BufferSlice create_empty<BufferSlice>(size_t size) {
|
||||
return BufferSlice{size};
|
||||
}
|
||||
|
||||
template <>
|
||||
SecureString create_empty<SecureString>(size_t size) {
|
||||
return SecureString{size};
|
||||
@ -41,16 +41,20 @@ SecureString create_empty<SecureString>(size_t size) {
|
||||
template <class T>
|
||||
Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
|
||||
TRY_RESULT(from_file, FileFd::open(path, FileFd::Read));
|
||||
TRY_RESULT(file_size, from_file.get_size());
|
||||
if (offset < 0 || offset > file_size) {
|
||||
return Status::Error("Failed to read file: invalid offset");
|
||||
}
|
||||
if (size == -1) {
|
||||
TRY_RESULT_ASSIGN(size, from_file.get_size());
|
||||
size = file_size - offset;
|
||||
} else if (size >= 0) {
|
||||
if (size + offset > file_size) {
|
||||
size = file_size - offset;
|
||||
}
|
||||
}
|
||||
if (size < 0) {
|
||||
return Status::Error("Failed to read file: invalid size");
|
||||
}
|
||||
if (offset < 0 || offset > size) {
|
||||
return Status::Error("Failed to read file: invalid offset");
|
||||
}
|
||||
size -= offset;
|
||||
auto content = create_empty<T>(narrow_cast<size_t>(size));
|
||||
TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset));
|
||||
if (got_size != static_cast<size_t>(size)) {
|
||||
@ -80,13 +84,23 @@ Status copy_file(CSlice from, CSlice to, int64 size) {
|
||||
return write_file(to, content.as_slice());
|
||||
}
|
||||
|
||||
Status write_file(CSlice to, Slice data) {
|
||||
Status write_file(CSlice to, Slice data, WriteFileOptions options) {
|
||||
auto size = data.size();
|
||||
TRY_RESULT(to_file, FileFd::open(to, FileFd::Truncate | FileFd::Create | FileFd::Write));
|
||||
if (options.need_lock) {
|
||||
TRY_STATUS(to_file.lock(FileFd::LockFlags::Write, to.str(), 10));
|
||||
TRY_STATUS(to_file.truncate_to_current_position(0));
|
||||
}
|
||||
TRY_RESULT(written, to_file.write(data));
|
||||
if (written != size) {
|
||||
return Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size);
|
||||
}
|
||||
if (options.need_sync) {
|
||||
TRY_STATUS(to_file.sync());
|
||||
}
|
||||
if (options.need_lock) {
|
||||
to_file.lock(FileFd::LockFlags::Unlock, to.str(), 10).ignore();
|
||||
}
|
||||
to_file.close();
|
||||
return Status::OK();
|
||||
}
|
||||
@ -166,4 +180,17 @@ string clean_filename(CSlice name) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
Status atomic_write_file(CSlice path, Slice data, CSlice path_tmp) {
|
||||
string path_tmp_buf;
|
||||
if (path_tmp.empty()) {
|
||||
path_tmp_buf = path.str() + ".tmp";
|
||||
path_tmp = path_tmp_buf;
|
||||
}
|
||||
|
||||
WriteFileOptions options;
|
||||
options.need_sync = true;
|
||||
options.need_lock = true;
|
||||
TRY_STATUS(write_file(path_tmp, data, options));
|
||||
return rename(path_tmp, path);
|
||||
}
|
||||
} // namespace td
|
||||
|
@ -9,18 +9,27 @@
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
Result<BufferSlice> read_file(CSlice path, int64 size = -1, int64 offset = 0);
|
||||
Result<SecureString> read_file_secure(CSlice path, int64 size = -1, int64 offset = 0);
|
||||
Result<string> read_file_str(CSlice path, int64 size = -1, int64 offset = 0);
|
||||
Result<SecureString> read_file_secure(CSlice path, int64 size = -1, int64 offset = 0);
|
||||
|
||||
Status copy_file(CSlice from, CSlice to, int64 size = -1) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Status write_file(CSlice to, Slice data) TD_WARN_UNUSED_RESULT;
|
||||
struct WriteFileOptions {
|
||||
bool need_sync = true;
|
||||
bool need_lock = true;
|
||||
};
|
||||
Status write_file(CSlice to, Slice data, WriteFileOptions options = {}) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
string clean_filename(CSlice name);
|
||||
|
||||
// write file and ensure that it either fully overriden with new data, or left intact.
|
||||
// Uses path_tmp to temporary storat data, than calls rename
|
||||
Status atomic_write_file(CSlice path, Slice data, CSlice path_tmp = {});
|
||||
|
||||
} // namespace td
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
|
||||
namespace td {
|
||||
namespace format {
|
||||
@ -334,5 +335,9 @@ template <class T>
|
||||
StringBuilder &operator<<(StringBuilder &stream, const vector<T> &vec) {
|
||||
return stream << format::as_array(vec);
|
||||
}
|
||||
template <class T>
|
||||
StringBuilder &operator<<(StringBuilder &stream, const std::set<T> &vec) {
|
||||
return stream << format::as_array(vec);
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -116,7 +116,7 @@ auto invoke(F &&f,
|
||||
}
|
||||
|
||||
template <class F, class... Args, std::size_t... S>
|
||||
auto call_tuple_impl(F &func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
|
||||
auto call_tuple_impl(F &&func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
|
||||
return func(std::forward<Args>(std::get<S>(tuple))...);
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ class LogicAnd {
|
||||
};
|
||||
|
||||
template <class F, class... Args>
|
||||
auto call_tuple(F &func, std::tuple<Args...> &&tuple) {
|
||||
auto call_tuple(F &&func, std::tuple<Args...> &&tuple) {
|
||||
return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange<sizeof...(Args)>());
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
|
||||
#if TD_ANDROID
|
||||
#include <android/log.h>
|
||||
@ -263,4 +264,26 @@ void process_fatal_error(CSlice message) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::mutex sdl_mutex;
|
||||
int sdl_cnt = 0;
|
||||
int sdl_verbosity = 0;
|
||||
|
||||
} // namespace
|
||||
ScopedDisableLog::ScopedDisableLog() {
|
||||
std::unique_lock<std::mutex> guard(sdl_mutex);
|
||||
if (sdl_cnt == 0) {
|
||||
sdl_verbosity = set_verbosity_level(std::numeric_limits<int>::min());
|
||||
}
|
||||
sdl_cnt++;
|
||||
}
|
||||
|
||||
ScopedDisableLog::~ScopedDisableLog() {
|
||||
std::unique_lock<std::mutex> guard(sdl_mutex);
|
||||
sdl_cnt--;
|
||||
if (sdl_cnt == 0) {
|
||||
set_verbosity_level(sdl_verbosity);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -40,8 +40,8 @@
|
||||
|
||||
#define VERBOSITY_NAME(x) verbosity_##x
|
||||
|
||||
#define GET_VERBOSITY_LEVEL() (::td::log_options.level)
|
||||
#define SET_VERBOSITY_LEVEL(new_level) (::td::log_options.level = (new_level))
|
||||
#define GET_VERBOSITY_LEVEL() (::td::get_verbosity_level())
|
||||
#define SET_VERBOSITY_LEVEL(new_level) (::td::set_verbosity_level(new_level))
|
||||
|
||||
#ifndef STRIP_LOG
|
||||
#define STRIP_LOG VERBOSITY_NAME(DEBUG)
|
||||
@ -52,7 +52,7 @@
|
||||
#define LOGGER(interface, options, level, comment) ::td::Logger(interface, options, level, __FILE__, __LINE__, comment)
|
||||
|
||||
#define LOG_IMPL_FULL(interface, options, strip_level, runtime_level, condition, comment) \
|
||||
LOG_IS_STRIPPED(strip_level) || runtime_level > options.level || !(condition) \
|
||||
LOG_IS_STRIPPED(strip_level) || runtime_level > options.get_level() || !(condition) \
|
||||
? (void)0 \
|
||||
: ::td::detail::Voidify() & LOGGER(interface, options, runtime_level, comment)
|
||||
|
||||
@ -121,11 +121,18 @@ extern int VERBOSITY_NAME(files);
|
||||
extern int VERBOSITY_NAME(sqlite);
|
||||
|
||||
struct LogOptions {
|
||||
int level{VERBOSITY_NAME(DEBUG) + 1};
|
||||
std::atomic<int> level{VERBOSITY_NAME(DEBUG) + 1};
|
||||
bool fix_newlines{true};
|
||||
bool add_info{true};
|
||||
|
||||
static constexpr LogOptions plain() {
|
||||
int get_level() const {
|
||||
return level.load(std::memory_order_relaxed);
|
||||
}
|
||||
int set_level(int new_level) {
|
||||
return level.exchange(new_level);
|
||||
}
|
||||
|
||||
static LogOptions plain() {
|
||||
return LogOptions{0, false, false};
|
||||
}
|
||||
|
||||
@ -133,9 +140,31 @@ struct LogOptions {
|
||||
constexpr LogOptions(int level, bool fix_newlines, bool add_info)
|
||||
: level(level), fix_newlines(fix_newlines), add_info(add_info) {
|
||||
}
|
||||
|
||||
LogOptions(const LogOptions &other) : LogOptions(other.level.load(), other.fix_newlines, other.add_info) {
|
||||
}
|
||||
|
||||
LogOptions &operator=(const LogOptions &other) {
|
||||
level = other.level.load();
|
||||
fix_newlines = other.fix_newlines;
|
||||
add_info = other.add_info;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
extern LogOptions log_options;
|
||||
inline int set_verbosity_level(int level) {
|
||||
return log_options.set_level(level);
|
||||
}
|
||||
inline int get_verbosity_level() {
|
||||
return log_options.get_level();
|
||||
}
|
||||
|
||||
class ScopedDisableLog {
|
||||
public:
|
||||
ScopedDisableLog();
|
||||
~ScopedDisableLog();
|
||||
};
|
||||
|
||||
class LogInterface {
|
||||
public:
|
||||
|
@ -32,12 +32,12 @@ std::pair<T, T> split(T s, char delimiter = ' ') {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
vector<T> full_split(T s, char delimiter = ' ') {
|
||||
vector<T> full_split(T s, char delimiter = ' ', size_t max_parts = std::numeric_limits<size_t>::max()) {
|
||||
vector<T> result;
|
||||
if (s.empty()) {
|
||||
return result;
|
||||
}
|
||||
while (true) {
|
||||
while (result.size() + 1 < max_parts) {
|
||||
auto delimiter_pos = s.find(delimiter);
|
||||
if (delimiter_pos == string::npos) {
|
||||
result.push_back(std::move(s));
|
||||
@ -47,6 +47,8 @@ vector<T> full_split(T s, char delimiter = ' ') {
|
||||
s = s.substr(delimiter_pos + 1);
|
||||
}
|
||||
}
|
||||
result.push_back(std::move(s));
|
||||
return result;
|
||||
}
|
||||
|
||||
string implode(const vector<string> &v, char delimiter = ' ');
|
||||
@ -193,10 +195,11 @@ inline char to_lower(char c) {
|
||||
return c;
|
||||
}
|
||||
|
||||
inline void to_lower_inplace(MutableSlice slice) {
|
||||
inline MutableSlice to_lower_inplace(MutableSlice slice) {
|
||||
for (auto &c : slice) {
|
||||
c = to_lower(c);
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
inline string to_lower(Slice slice) {
|
||||
@ -340,6 +343,20 @@ typename std::enable_if<std::is_unsigned<T>::value, T>::type hex_to_integer(Slic
|
||||
return integer_value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Result<typename std::enable_if<std::is_unsigned<T>::value, T>::type> hex_to_integer_safe(Slice str) {
|
||||
T integer_value = 0;
|
||||
auto begin = str.begin();
|
||||
auto end = str.end();
|
||||
while (begin != end) {
|
||||
if (!is_hex_digit(*begin)) {
|
||||
return Status::Error("not a hex digit");
|
||||
}
|
||||
integer_value = static_cast<T>(integer_value * 16 + hex_to_int(*begin++));
|
||||
}
|
||||
return integer_value;
|
||||
}
|
||||
|
||||
double to_double(Slice str);
|
||||
|
||||
template <class T>
|
||||
@ -363,8 +380,8 @@ string url_encode(Slice data);
|
||||
|
||||
namespace detail {
|
||||
template <class T, class U>
|
||||
struct is_same_signedness
|
||||
: public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {};
|
||||
struct is_same_signedness : public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {
|
||||
};
|
||||
|
||||
template <class T, class Enable = void>
|
||||
struct safe_undeflying_type {
|
||||
|
@ -47,9 +47,11 @@ class optional {
|
||||
return impl_.is_ok();
|
||||
}
|
||||
T &value() {
|
||||
DCHECK(*this);
|
||||
return impl_.ok_ref();
|
||||
}
|
||||
const T &value() const {
|
||||
DCHECK(*this);
|
||||
return impl_.ok_ref();
|
||||
}
|
||||
T &operator*() {
|
||||
@ -62,6 +64,13 @@ class optional {
|
||||
return res;
|
||||
}
|
||||
|
||||
td::optional<T> copy() const {
|
||||
if (*this) {
|
||||
return value();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class... ArgsT>
|
||||
void emplace(ArgsT &&... args) {
|
||||
impl_.emplace(std::forward<ArgsT>(args)...);
|
||||
|
@ -281,6 +281,17 @@ Result<size_t> FileFd::read(MutableSlice slice) {
|
||||
#if TD_PORT_POSIX
|
||||
auto bytes_read = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
|
||||
bool success = bytes_read >= 0;
|
||||
if (!success) {
|
||||
auto read_errno = errno;
|
||||
if (read_errno == EAGAIN
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
|| read_errno == EWOULDBLOCK
|
||||
#endif
|
||||
) {
|
||||
success = true;
|
||||
bytes_read = 0;
|
||||
}
|
||||
}
|
||||
bool is_eof = success && narrow_cast<size_t>(bytes_read) < slice.size();
|
||||
#elif TD_PORT_WINDOWS
|
||||
DWORD bytes_read = 0;
|
||||
|
@ -82,7 +82,7 @@ Result<MemoryMapping> MemoryMapping::create_from_file(const FileFd &file_fd, con
|
||||
auto fixed_begin = begin / page_size * page_size;
|
||||
|
||||
auto data_offset = begin - fixed_begin;
|
||||
auto data_size = narrow_cast<size_t>(end - fixed_begin);
|
||||
TRY_RESULT(data_size, narrow_cast_safe<size_t>(end - fixed_begin));
|
||||
|
||||
void *data = mmap(nullptr, data_size, PROT_READ, MAP_PRIVATE, fd, narrow_cast<off_t>(fixed_begin));
|
||||
if (data == MAP_FAILED) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -22,9 +23,7 @@ namespace td {
|
||||
template <int id>
|
||||
static FileFd &get_file_fd() {
|
||||
static FileFd result = FileFd::from_native_fd(NativeFd(id, true));
|
||||
static auto guard = ScopeExit() + [&] {
|
||||
result.move_as_native_fd().release();
|
||||
};
|
||||
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -44,9 +43,7 @@ static FileFd &get_file_fd() {
|
||||
static auto handle = GetStdHandle(id);
|
||||
LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to GetStdHandle " << id;
|
||||
static FileFd result = FileFd::from_native_fd(NativeFd(handle, true));
|
||||
static auto guard = ScopeExit() + [&] {
|
||||
result.move_as_native_fd().release();
|
||||
};
|
||||
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
#else
|
||||
static FileFd result;
|
||||
#endif
|
||||
|
@ -100,6 +100,7 @@ void Epoll::run(int timeout_ms) {
|
||||
#ifdef EPOLLRDHUP
|
||||
if (event->events & EPOLLRDHUP) {
|
||||
event->events &= ~EPOLLRDHUP;
|
||||
flags = flags | PollFlags::Close();
|
||||
// flags |= Fd::Close;
|
||||
// TODO
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/port/detail/ThreadPthread.h"
|
||||
|
||||
char disable_linker_warning_about_empty_file_thread_pthread_cpp TD_UNUSED;
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/port/rlimit.h"
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
@ -19,39 +20,76 @@
|
||||
namespace td {
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
static int get_resource(ResourceLimitType type) {
|
||||
switch (type) {
|
||||
|
||||
namespace {
|
||||
|
||||
int get_rlimit_type(ResourceLimitType rlim_type) {
|
||||
switch (rlim_type) {
|
||||
case ResourceLimitType::NoFile:
|
||||
return RLIMIT_NOFILE;
|
||||
case ResourceLimitType::Rss:
|
||||
return RLIMIT_RSS;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Status set_resource_limit(ResourceLimitType type, uint64 value) {
|
||||
#if TD_PORT_POSIX
|
||||
int resource = get_resource(type);
|
||||
} // namespace
|
||||
|
||||
rlimit rlim;
|
||||
if (getrlimit(resource, &rlim) == -1) {
|
||||
return OS_ERROR("Failed to get current resource limit");
|
||||
td::Status set_resource_limit(ResourceLimitType rlim_type, td::uint64 value, td::uint64 cap) {
|
||||
if (cap && value > cap) {
|
||||
return td::Status::Error("setrlimit(): bad argument");
|
||||
}
|
||||
int resource = get_rlimit_type(rlim_type);
|
||||
|
||||
struct rlimit r;
|
||||
if (getrlimit(resource, &r) < 0) {
|
||||
return td::Status::PosixError(errno, "failed getrlimit()");
|
||||
}
|
||||
|
||||
TRY_RESULT(new_value, narrow_cast_safe<rlim_t>(value));
|
||||
if (rlim.rlim_max < new_value) {
|
||||
rlim.rlim_max = new_value;
|
||||
if (cap) {
|
||||
r.rlim_max = cap;
|
||||
} else if (r.rlim_max < value) {
|
||||
r.rlim_max = value;
|
||||
}
|
||||
rlim.rlim_cur = new_value;
|
||||
|
||||
if (setrlimit(resource, &rlim) < 0) {
|
||||
return OS_ERROR("Failed to set resource limit");
|
||||
r.rlim_cur = value;
|
||||
if (setrlimit(resource, &r) < 0) {
|
||||
return td::Status::PosixError(errno, "failed setrlimit()");
|
||||
}
|
||||
return Status::OK();
|
||||
#elif TD_PORT_WINDOWS
|
||||
return Status::OK(); // Windows has no limits
|
||||
#endif
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status set_maximize_resource_limit(ResourceLimitType rlim_type, td::uint64 value) {
|
||||
int resource = get_rlimit_type(rlim_type);
|
||||
|
||||
struct rlimit r;
|
||||
if (getrlimit(resource, &r) < 0) {
|
||||
return td::Status::PosixError(errno, "failed getrlimit()");
|
||||
}
|
||||
|
||||
if (r.rlim_max < value) {
|
||||
auto t = r;
|
||||
t.rlim_cur = value;
|
||||
t.rlim_max = value;
|
||||
if (setrlimit(resource, &t) >= 0) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
r.rlim_cur = value < r.rlim_max ? value : r.rlim_max;
|
||||
if (setrlimit(resource, &r) < 0) {
|
||||
return td::Status::PosixError(errno, "failed setrlimit()");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
#else
|
||||
td::Status set_resource_limit(ResourceLimitType rlim, td::uint64 value) {
|
||||
return td::Status::Error("setrlimit not implemented on WINDOWS");
|
||||
}
|
||||
td::Status set_maximize_resource_limit(ResourceLimitType rlim, td::uint64 value) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace td
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
@ -11,8 +12,8 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
enum class ResourceLimitType { NoFile };
|
||||
|
||||
Status set_resource_limit(ResourceLimitType type, uint64 value);
|
||||
enum class ResourceLimitType { NoFile, Rss };
|
||||
|
||||
td::Status set_resource_limit(ResourceLimitType rlim_type, td::uint64 value, td::uint64 cap = 0);
|
||||
td::Status set_maximize_resource_limit(ResourceLimitType rlim, td::uint64 value);
|
||||
} // namespace td
|
||||
|
@ -318,6 +318,9 @@ static void default_failure_signal_handler(int sig) {
|
||||
}
|
||||
|
||||
Status set_default_failure_signal_handler() {
|
||||
#if TD_PORT_POSIX
|
||||
Stdin(); // init static variables before atexit
|
||||
#endif
|
||||
std::atexit(block_stdin);
|
||||
TRY_STATUS(setup_signals_alt_stack());
|
||||
TRY_STATUS(set_signal_handler(SignalType::Abort, default_failure_signal_handler));
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/port/user.h"
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
|
@ -4,13 +4,12 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
Status change_user(CSlice username, CSlice groupname = CSlice());
|
||||
|
||||
}
|
||||
|
@ -192,6 +192,7 @@ bool TestsRunner::run_all_step() {
|
||||
}
|
||||
LOG(ERROR) << "Run test " << tag("name", name);
|
||||
state_.start = Time::now();
|
||||
state_.start_unadjusted = Time::now_unadjusted();
|
||||
state_.is_running = true;
|
||||
}
|
||||
|
||||
@ -199,7 +200,13 @@ bool TestsRunner::run_all_step() {
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(ERROR) << format::as_time(Time::now() - state_.start);
|
||||
auto passed = Time::now() - state_.start;
|
||||
auto real_passed = Time::now_unadjusted() - state_.start_unadjusted;
|
||||
if (real_passed + 1e-9 > passed) {
|
||||
LOG(ERROR) << format::as_time(passed);
|
||||
} else {
|
||||
LOG(ERROR) << format::as_time(passed) << " real[" << format::as_time(real_passed) << "]";
|
||||
}
|
||||
if (regression_tester_) {
|
||||
regression_tester_->save_db();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Span.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -27,6 +28,34 @@
|
||||
|
||||
namespace td {
|
||||
|
||||
class RandomSteps {
|
||||
public:
|
||||
struct Step {
|
||||
std::function<void()> func;
|
||||
td::uint32 weight;
|
||||
};
|
||||
RandomSteps(std::vector<Step> steps) : steps_(std::move(steps)) {
|
||||
for (auto &step : steps_) {
|
||||
steps_sum_ += step.weight;
|
||||
}
|
||||
}
|
||||
template <class Random>
|
||||
void step(Random &rnd) {
|
||||
auto w = rnd() % steps_sum_;
|
||||
for (auto &step : steps_) {
|
||||
if (w < step.weight) {
|
||||
step.func();
|
||||
break;
|
||||
}
|
||||
w -= step.weight;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Step> steps_;
|
||||
td::int32 steps_sum_ = 0;
|
||||
};
|
||||
|
||||
class RegressionTester {
|
||||
public:
|
||||
virtual ~RegressionTester() = default;
|
||||
@ -78,6 +107,7 @@ class TestsRunner : public TestContext {
|
||||
size_t it{0};
|
||||
bool is_running = false;
|
||||
double start{0};
|
||||
double start_unadjusted{0};
|
||||
size_t end{0};
|
||||
};
|
||||
bool stress_flag_{false};
|
||||
@ -135,36 +165,6 @@ inline vector<string> rand_split(Slice str) {
|
||||
return res;
|
||||
}
|
||||
|
||||
struct Step {
|
||||
std::function<void()> func;
|
||||
uint32 weight;
|
||||
};
|
||||
|
||||
class RandomSteps {
|
||||
public:
|
||||
explicit RandomSteps(vector<Step> steps) : steps_(std::move(steps)) {
|
||||
for (const auto &step : steps_) {
|
||||
steps_sum_ += step.weight;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Random>
|
||||
void step(Random &rnd) const {
|
||||
auto w = rnd() % steps_sum_;
|
||||
for (const auto &step : steps_) {
|
||||
if (w < step.weight) {
|
||||
step.func();
|
||||
break;
|
||||
}
|
||||
w -= step.weight;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
vector<Step> steps_;
|
||||
int32 steps_sum_ = 0;
|
||||
};
|
||||
|
||||
template <class T1, class T2>
|
||||
void assert_eq_impl(const T1 &expected, const T2 &got, const char *file, int line) {
|
||||
LOG_CHECK(expected == got) << tag("expected", expected) << tag("got", got) << " in " << file << " at line " << line;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/tl_parsers.h"
|
||||
@ -21,10 +22,10 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#define BEGIN_STORE_FLAGS() \
|
||||
do { \
|
||||
uint32 flags_store = 0; \
|
||||
uint32 bit_offset_store = 0
|
||||
#define BEGIN_STORE_FLAGS() \
|
||||
do { \
|
||||
td::uint32 flags_store = 0; \
|
||||
td::uint32 bit_offset_store = 0
|
||||
|
||||
#define STORE_FLAG(flag) \
|
||||
flags_store |= (flag) << bit_offset_store; \
|
||||
@ -36,10 +37,10 @@
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define BEGIN_PARSE_FLAGS() \
|
||||
do { \
|
||||
uint32 flags_parse; \
|
||||
uint32 bit_offset_parse = 0; \
|
||||
#define BEGIN_PARSE_FLAGS() \
|
||||
do { \
|
||||
td::uint32 flags_parse; \
|
||||
td::uint32 bit_offset_parse = 0; \
|
||||
td::parse(flags_parse, parser)
|
||||
|
||||
#define PARSE_FLAG(flag) \
|
||||
|
@ -70,6 +70,16 @@ class TlParser {
|
||||
}
|
||||
}
|
||||
|
||||
bool can_prefetch_int() const {
|
||||
return get_left_len() >= sizeof(int32);
|
||||
}
|
||||
|
||||
int32 prefetch_int_unsafe() const {
|
||||
int32 result;
|
||||
std::memcpy(&result, data, sizeof(int32));
|
||||
return result;
|
||||
}
|
||||
|
||||
int32 fetch_int_unsafe() {
|
||||
int32 result;
|
||||
std::memcpy(&result, data, sizeof(int32));
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "td/utils/StorerBase.h"
|
||||
#include "td/utils/UInt.h"
|
||||
|
||||
#include "td/utils/SharedSlice.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace td {
|
||||
@ -214,6 +216,12 @@ class TlStorerToString {
|
||||
store_field_end();
|
||||
}
|
||||
|
||||
void store_field(const char *name, const SecureString &value) {
|
||||
store_field_begin(name);
|
||||
result.append("<secret>");
|
||||
store_field_end();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void store_field(const char *name, const T &value) {
|
||||
store_field_begin(name);
|
||||
@ -221,6 +229,12 @@ class TlStorerToString {
|
||||
store_field_end();
|
||||
}
|
||||
|
||||
void store_bytes_field(const char *name, const SecureString &value) {
|
||||
store_field_begin(name);
|
||||
result.append("<secret>");
|
||||
store_field_end();
|
||||
}
|
||||
|
||||
template <class BytesT>
|
||||
void store_bytes_field(const char *name, const BytesT &value) {
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
|
@ -30,12 +30,18 @@ class uint128_emulated {
|
||||
uint64 lo() const {
|
||||
return lo_;
|
||||
}
|
||||
uint64 rounded_hi() const {
|
||||
return hi_ + (lo_ >> 63);
|
||||
}
|
||||
static uint128 from_signed(int64 x) {
|
||||
if (x >= 0) {
|
||||
return uint128(0, x);
|
||||
}
|
||||
return uint128(std::numeric_limits<uint64>::max(), static_cast<uint64>(x));
|
||||
}
|
||||
static uint128 from_unsigned(uint64 x) {
|
||||
return uint128(0, x);
|
||||
}
|
||||
|
||||
uint128 add(uint128 other) const {
|
||||
uint128 res(other.hi() + hi(), other.lo() + lo());
|
||||
@ -200,13 +206,18 @@ class uint128_intrinsic {
|
||||
static uint128 from_signed(int64 x) {
|
||||
return uint128(static_cast<ValueT>(x));
|
||||
}
|
||||
static uint128 from_unsigned(uint64 x) {
|
||||
return uint128(static_cast<ValueT>(x));
|
||||
}
|
||||
uint64 hi() const {
|
||||
return uint64(value() >> 64);
|
||||
}
|
||||
uint64 lo() const {
|
||||
return uint64(value() & std::numeric_limits<uint64>::max());
|
||||
}
|
||||
|
||||
uint64 rounded_hi() const {
|
||||
return uint64((value() + (1ULL << 63)) >> 64);
|
||||
}
|
||||
uint128 add(uint128 other) const {
|
||||
return uint128(value() + other.value());
|
||||
}
|
||||
|
@ -252,4 +252,4 @@ TEST(ConcurrentHashMap, Benchmark) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/List.h"
|
||||
#include "td/utils/MovableValue.h"
|
||||
|
@ -13,21 +13,22 @@
|
||||
#include <atomic>
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
TEST(MpmcWaiter, stress_one_one) {
|
||||
template <class W>
|
||||
void test_waiter_stress_one_one() {
|
||||
td::Stage run;
|
||||
td::Stage check;
|
||||
|
||||
std::vector<td::thread> threads;
|
||||
std::atomic<size_t> value{0};
|
||||
size_t write_cnt = 10;
|
||||
td::unique_ptr<td::MpmcWaiter> waiter;
|
||||
td::unique_ptr<W> waiter;
|
||||
size_t threads_n = 2;
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
|
||||
for (td::uint64 round = 1; round < 100000; round++) {
|
||||
if (id == 0) {
|
||||
value = 0;
|
||||
waiter = td::make_unique<td::MpmcWaiter>();
|
||||
waiter = td::make_unique<W>();
|
||||
write_cnt = td::Random::fast(1, 10);
|
||||
}
|
||||
run.wait(round * threads_n);
|
||||
@ -37,17 +38,19 @@ TEST(MpmcWaiter, stress_one_one) {
|
||||
waiter->notify();
|
||||
}
|
||||
} else {
|
||||
int yields = 0;
|
||||
typename W::Slot slot;
|
||||
waiter->init_slot(slot, id);
|
||||
for (size_t i = 1; i <= write_cnt; i++) {
|
||||
while (true) {
|
||||
auto x = value.load(std::memory_order_relaxed);
|
||||
if (x >= i) {
|
||||
break;
|
||||
}
|
||||
yields = waiter->wait(yields, id);
|
||||
waiter->wait(slot);
|
||||
}
|
||||
yields = waiter->stop_wait(yields, id);
|
||||
waiter->stop_wait(slot);
|
||||
}
|
||||
waiter->stop_wait(slot);
|
||||
}
|
||||
check.wait(round * threads_n);
|
||||
}
|
||||
@ -57,7 +60,15 @@ TEST(MpmcWaiter, stress_one_one) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
TEST(MpmcWaiter, stress) {
|
||||
TEST(MpmcEagerWaiter, stress_one_one) {
|
||||
test_waiter_stress_one_one<td::MpmcEagerWaiter>();
|
||||
}
|
||||
TEST(MpmcSleepyWaiter, stress_one_one) {
|
||||
test_waiter_stress_one_one<td::MpmcSleepyWaiter>();
|
||||
}
|
||||
|
||||
template <class W>
|
||||
void test_waiter_stress() {
|
||||
td::Stage run;
|
||||
td::Stage check;
|
||||
|
||||
@ -69,7 +80,7 @@ TEST(MpmcWaiter, stress) {
|
||||
size_t end_pos;
|
||||
size_t write_cnt;
|
||||
size_t threads_n = 20;
|
||||
td::unique_ptr<td::MpmcWaiter> waiter;
|
||||
td::unique_ptr<W> waiter;
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
|
||||
for (td::uint64 round = 1; round < 1000; round++) {
|
||||
@ -80,7 +91,7 @@ TEST(MpmcWaiter, stress) {
|
||||
end_pos = write_n * write_cnt;
|
||||
write_pos = 0;
|
||||
read_pos = 0;
|
||||
waiter = td::make_unique<td::MpmcWaiter>();
|
||||
waiter = td::make_unique<W>();
|
||||
}
|
||||
run.wait(round * threads_n);
|
||||
if (id <= write_n) {
|
||||
@ -92,21 +103,26 @@ TEST(MpmcWaiter, stress) {
|
||||
waiter->notify();
|
||||
}
|
||||
} else if (id > 10 && id - 10 <= read_n) {
|
||||
int yields = 0;
|
||||
typename W::Slot slot;
|
||||
waiter->init_slot(slot, id);
|
||||
while (true) {
|
||||
auto x = read_pos.load(std::memory_order_relaxed);
|
||||
if (x == end_pos) {
|
||||
waiter->stop_wait(slot);
|
||||
break;
|
||||
}
|
||||
if (x == write_pos.load(std::memory_order_relaxed)) {
|
||||
yields = waiter->wait(yields, id);
|
||||
waiter->wait(slot);
|
||||
continue;
|
||||
}
|
||||
yields = waiter->stop_wait(yields, id);
|
||||
waiter->stop_wait(slot);
|
||||
read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
check.wait(round * threads_n);
|
||||
if (id == 0) {
|
||||
waiter->close();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -114,4 +130,10 @@ TEST(MpmcWaiter, stress) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
TEST(MpmcEagerWaiter, stress_multi) {
|
||||
test_waiter_stress<td::MpmcEagerWaiter>();
|
||||
}
|
||||
TEST(MpmcSleepyWaiter, stress_multi) {
|
||||
test_waiter_stress<td::MpmcSleepyWaiter>();
|
||||
}
|
||||
#endif // !TD_THREAD_UNSUPPORTED
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/OptionParser.h"
|
||||
@ -47,23 +48,13 @@ TEST(OptionParser, run) {
|
||||
ASSERT_TRUE(result.is_error());
|
||||
};
|
||||
|
||||
options.add_option('q', "", "", [&] {
|
||||
chosen_options += 1;
|
||||
return td::Status::OK();
|
||||
});
|
||||
options.add_option('\0', "http-port2", "", [&] {
|
||||
chosen_options += 10;
|
||||
return td::Status::OK();
|
||||
});
|
||||
options.add_option('q', "", "", [&] { chosen_options += 1; });
|
||||
options.add_option('\0', "http-port2", "", [&] { chosen_options += 10; });
|
||||
options.add_option('p', "http-port", "", [&](td::Slice parameter) {
|
||||
chosen_options += 100;
|
||||
chosen_parameters.push_back(parameter.str());
|
||||
return td::Status::OK();
|
||||
});
|
||||
options.add_option('v', "test", "", [&] {
|
||||
chosen_options += 1000;
|
||||
return td::Status::OK();
|
||||
});
|
||||
options.add_option('v', "test", "", [&] { chosen_options += 1000; });
|
||||
|
||||
test_fail("-http-port2");
|
||||
test_success("-", 0, {}, {"-"});
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
|
@ -23,13 +23,9 @@ TEST(Heap, sort_random_perm) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
v[i] = i;
|
||||
}
|
||||
|
||||
// random shuffle
|
||||
for (int i = 1; i < n; i++) {
|
||||
std::swap(v[td::Random::fast(0, i)], v[i]);
|
||||
}
|
||||
|
||||
td::vector<td::HeapNode> nodes(n);
|
||||
td::Random::Xorshift128plus rnd(123);
|
||||
td::random_shuffle(td::as_mutable_span(v), rnd);
|
||||
std::vector<td::HeapNode> nodes(n);
|
||||
td::KHeap<int> kheap;
|
||||
for (int i = 0; i < n; i++) {
|
||||
kheap.insert(v[i], &nodes[i]);
|
||||
|
@ -4,6 +4,7 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/FileLog.h"
|
||||
#include "td/utils/format.h"
|
||||
@ -17,11 +18,6 @@
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
// Thread safe logging with tests
|
||||
//
|
||||
// LOG uses thread local LogInterface
|
||||
// void append(CSlice slice, int log_level);
|
||||
|
||||
char disable_linker_warning_about_empty_file_tdutils_test_log_cpp TD_UNUSED;
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
@ -56,6 +52,9 @@ class LogBenchmark : public td::Benchmark {
|
||||
void run_thread(int n) {
|
||||
auto str = PSTRING() << "#" << n << " : fsjklfdjsklfjdsklfjdksl\n";
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i % 10000 == 0) {
|
||||
log_->rotate();
|
||||
}
|
||||
log_->append(str);
|
||||
}
|
||||
}
|
||||
@ -74,7 +73,8 @@ static void bench_log(std::string name, int threads_n, F &&f) {
|
||||
};
|
||||
|
||||
TEST(Log, TsLogger) {
|
||||
bench_log("NewTsFileLog", 4, [] { return td::TsFileLog::create("tmplog").move_as_ok(); });
|
||||
bench_log("NewTsFileLog", 4,
|
||||
[] { return td::TsFileLog::create("tmplog", std::numeric_limits<td::int64>::max(), false).move_as_ok(); });
|
||||
bench_log("TsFileLog", 8, [] {
|
||||
class FileLog : public td::LogInterface {
|
||||
public:
|
||||
|
@ -154,8 +154,7 @@ TEST(Misc, get_last_argument) {
|
||||
}
|
||||
|
||||
TEST(Misc, call_n_arguments) {
|
||||
auto f = [](int, int) {
|
||||
};
|
||||
auto f = [](int, int) {};
|
||||
call_n_arguments<2>(f, 1, 3, 4);
|
||||
}
|
||||
|
||||
@ -254,18 +253,10 @@ static void test_remove_if(vector<int> v, const T &func, vector<int> expected) {
|
||||
}
|
||||
|
||||
TEST(Misc, remove_if) {
|
||||
auto odd = [](int x) {
|
||||
return x % 2 == 1;
|
||||
};
|
||||
auto even = [](int x) {
|
||||
return x % 2 == 0;
|
||||
};
|
||||
auto all = [](int x) {
|
||||
return true;
|
||||
};
|
||||
auto none = [](int x) {
|
||||
return false;
|
||||
};
|
||||
auto odd = [](int x) { return x % 2 == 1; };
|
||||
auto even = [](int x) { return x % 2 == 0; };
|
||||
auto all = [](int x) { return true; };
|
||||
auto none = [](int x) { return false; };
|
||||
|
||||
vector<int> v{1, 2, 3, 4, 5, 6};
|
||||
test_remove_if(v, odd, {2, 4, 6});
|
||||
@ -348,6 +339,26 @@ TEST(Misc, contains) {
|
||||
ASSERT_TRUE(td::contains(str, 'c'));
|
||||
}
|
||||
|
||||
TEST(Misc, base32) {
|
||||
ASSERT_EQ("", base32_encode(""));
|
||||
ASSERT_EQ("me", base32_encode("a"));
|
||||
base32_decode("me").ensure();
|
||||
ASSERT_EQ("mfra", base32_encode("ab"));
|
||||
ASSERT_EQ("mfrgg", base32_encode("abc"));
|
||||
ASSERT_EQ("mfrggza", base32_encode("abcd"));
|
||||
ASSERT_EQ("mfrggzdg", base32_encode("abcdf"));
|
||||
ASSERT_EQ("mfrggzdgm4", base32_encode("abcdfg"));
|
||||
for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) {
|
||||
for (int t = 0; t < 10; t++) {
|
||||
string s = rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
|
||||
auto encoded = base32_encode(s);
|
||||
auto decoded = base32_decode(encoded);
|
||||
ASSERT_TRUE(decoded.is_ok());
|
||||
ASSERT_TRUE(decoded.ok() == s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Misc, to_integer) {
|
||||
ASSERT_EQ(to_integer<int32>("-1234567"), -1234567);
|
||||
ASSERT_EQ(to_integer<int64>("-1234567"), -1234567);
|
||||
@ -721,6 +732,10 @@ static void test_full_split(Slice str, vector<Slice> expected) {
|
||||
ASSERT_EQ(expected, td::full_split(str));
|
||||
}
|
||||
|
||||
static void test_full_split(Slice str, char c, size_t max_parts, vector<Slice> expected) {
|
||||
ASSERT_EQ(expected, td::full_split(str, c, max_parts));
|
||||
}
|
||||
|
||||
TEST(Misc, full_split) {
|
||||
test_full_split("", {});
|
||||
test_full_split(" ", {"", ""});
|
||||
@ -736,6 +751,7 @@ TEST(Misc, full_split) {
|
||||
test_full_split(" abcdef ", {"", "abcdef", ""});
|
||||
test_full_split(" ab cd ef ", {"", "ab", "cd", "ef", ""});
|
||||
test_full_split(" ab cd ef ", {"", "", "ab", "", "cd", "", "ef", "", ""});
|
||||
test_full_split("ab cd ef gh", ' ', 3, {"ab", "cd", "ef gh"});
|
||||
}
|
||||
|
||||
TEST(Misc, StringBuilder) {
|
||||
@ -844,6 +860,39 @@ TEST(Misc, Bits) {
|
||||
ASSERT_EQ(4, count_bits64((1ull << 63) | 7));
|
||||
}
|
||||
|
||||
TEST(Misc, BitsRange) {
|
||||
auto to_vec_a = [](td::uint64 x) {
|
||||
std::vector<td::int32> bits;
|
||||
for (auto i : td::BitsRange(x)) {
|
||||
bits.push_back(i);
|
||||
}
|
||||
return bits;
|
||||
};
|
||||
|
||||
auto to_vec_b = [](td::uint64 x) {
|
||||
std::vector<td::int32> bits;
|
||||
td::int32 pos = 0;
|
||||
while (x != 0) {
|
||||
if ((x & 1) != 0) {
|
||||
bits.push_back(pos);
|
||||
}
|
||||
x >>= 1;
|
||||
pos++;
|
||||
}
|
||||
return bits;
|
||||
};
|
||||
|
||||
auto do_check = [](std::vector<td::int32> a, std::vector<td::int32> b) { ASSERT_EQ(b, a); };
|
||||
auto check = [&](td::uint64 x) { do_check(to_vec_a(x), to_vec_b(x)); };
|
||||
|
||||
do_check(to_vec_a(21), {0, 2, 4});
|
||||
for (int x = 0; x < 100; x++) {
|
||||
check(x);
|
||||
check(std::numeric_limits<td::uint32>::max() - x);
|
||||
check(std::numeric_limits<td::uint64>::max() - x);
|
||||
}
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
TEST(Misc, Time) {
|
||||
Stage run;
|
||||
@ -905,12 +954,8 @@ TEST(Misc, uint128) {
|
||||
static_cast<int64>(std::numeric_limits<int32>::min()) - 1};
|
||||
|
||||
#if TD_HAVE_INT128
|
||||
auto to_intrinsic = [](uint128_emulated num) {
|
||||
return uint128_intrinsic(num.hi(), num.lo());
|
||||
};
|
||||
auto eq = [](uint128_emulated a, uint128_intrinsic b) {
|
||||
return a.hi() == b.hi() && a.lo() == b.lo();
|
||||
};
|
||||
auto to_intrinsic = [](uint128_emulated num) { return uint128_intrinsic(num.hi(), num.lo()); };
|
||||
auto eq = [](uint128_emulated a, uint128_intrinsic b) { return a.hi() == b.hi() && a.lo() == b.lo(); };
|
||||
auto ensure_eq = [&](uint128_emulated a, uint128_intrinsic b) {
|
||||
if (!eq(a, b)) {
|
||||
LOG(FATAL) << "[" << a.hi() << ";" << a.lo() << "] vs [" << b.hi() << ";" << b.lo() << "]";
|
||||
@ -1102,6 +1147,13 @@ TEST(Misc, CancellationToken) {
|
||||
CHECK(token4);
|
||||
}
|
||||
|
||||
TEST(Misc, Xorshift128plus) {
|
||||
Random::Xorshift128plus rnd(123);
|
||||
ASSERT_EQ(11453256657207062272ull, rnd());
|
||||
ASSERT_EQ(14917490455889357332ull, rnd());
|
||||
ASSERT_EQ(5645917797309401285ull, rnd());
|
||||
ASSERT_EQ(13554822455746959330ull, rnd());
|
||||
}
|
||||
TEST(Misc, uname) {
|
||||
auto first_version = get_operating_system_version();
|
||||
auto second_version = get_operating_system_version();
|
||||
|
@ -26,9 +26,9 @@ int main(int argc, char **argv) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
|
||||
td::OptionParser options;
|
||||
options.add_option('\0', "filter", "Run only specified tests",
|
||||
options.add_option('f', "filter", "Run only specified tests",
|
||||
[&](td::Slice filter) { runner.add_substr_filter(filter.str()); });
|
||||
options.add_option('\0', "stress", "Run tests infinitely", [&] { runner.set_stress_flag(true); });
|
||||
options.add_option('s', "stress", "Run tests infinitely", [&] { runner.set_stress_flag(true); });
|
||||
auto r_non_options = options.run(argc, argv, 0);
|
||||
if (r_non_options.is_error()) {
|
||||
LOG(PLAIN) << argv[0] << ": " << r_non_options.error().message();
|
||||
|
Loading…
Reference in New Issue
Block a user