2018-08-13 19:15:09 +02:00
|
|
|
//
|
2020-01-01 02:23:48 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2020
|
2018-08-13 19:15:09 +02:00
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
//
|
2018-09-07 02:41:21 +02:00
|
|
|
#include "td/utils/common.h"
|
2018-09-10 03:08:15 +02:00
|
|
|
#include "td/utils/logging.h"
|
2019-05-22 20:17:24 +02:00
|
|
|
#include "td/utils/misc.h"
|
2020-08-07 17:50:33 +02:00
|
|
|
#include "td/utils/port/EventFd.h"
|
2018-08-13 19:15:09 +02:00
|
|
|
#include "td/utils/port/FileFd.h"
|
2019-07-23 00:50:12 +02:00
|
|
|
#include "td/utils/port/IoSlice.h"
|
2018-08-13 19:15:09 +02:00
|
|
|
#include "td/utils/port/path.h"
|
2019-07-06 13:29:15 +02:00
|
|
|
#include "td/utils/port/signals.h"
|
2020-08-07 17:50:33 +02:00
|
|
|
#include "td/utils/port/sleep.h"
|
2019-07-23 00:50:12 +02:00
|
|
|
#include "td/utils/port/thread.h"
|
|
|
|
#include "td/utils/port/thread_local.h"
|
2018-09-07 02:41:21 +02:00
|
|
|
#include "td/utils/Slice.h"
|
|
|
|
#include "td/utils/tests.h"
|
2020-08-07 17:50:33 +02:00
|
|
|
#include "td/utils/Time.h"
|
2018-08-13 19:15:09 +02:00
|
|
|
|
2020-08-07 17:50:33 +02:00
|
|
|
#include <atomic>
|
2019-08-14 02:13:34 +02:00
|
|
|
#include <set>
|
|
|
|
|
2018-08-13 19:15:09 +02:00
|
|
|
using namespace td;
|
|
|
|
|
|
|
|
TEST(Port, files) {
|
|
|
|
CSlice main_dir = "test_dir";
|
|
|
|
rmrf(main_dir).ignore();
|
2018-09-11 21:17:01 +02:00
|
|
|
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
|
2019-05-01 11:23:19 +02:00
|
|
|
ASSERT_TRUE(walk_path(main_dir, [](CSlice name, WalkPath::Type type) { UNREACHABLE(); }).is_error());
|
2018-08-13 19:15:09 +02:00
|
|
|
mkdir(main_dir).ensure();
|
|
|
|
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure();
|
|
|
|
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure();
|
2018-09-12 02:21:23 +02:00
|
|
|
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B" << TD_DIR_SLASH << "D").ensure();
|
2018-08-13 19:15:09 +02:00
|
|
|
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure();
|
|
|
|
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
|
|
|
|
std::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt";
|
2018-09-12 02:21:23 +02:00
|
|
|
std::string fd2_path = PSTRING() << main_dir << TD_DIR_SLASH << "C" << TD_DIR_SLASH << "t2.txt";
|
2018-08-13 19:15:09 +02:00
|
|
|
|
|
|
|
auto fd = FileFd::open(fd_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
2018-09-12 02:21:23 +02:00
|
|
|
auto fd2 = FileFd::open(fd2_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
|
|
|
fd2.close();
|
2018-09-11 21:17:01 +02:00
|
|
|
|
|
|
|
int cnt = 0;
|
|
|
|
const int ITER_COUNT = 1000;
|
|
|
|
for (int i = 0; i < ITER_COUNT; i++) {
|
2019-09-28 04:14:21 +02:00
|
|
|
walk_path(main_dir, [&](CSlice name, WalkPath::Type type) {
|
|
|
|
if (type == WalkPath::Type::NotDir) {
|
|
|
|
ASSERT_TRUE(name == fd_path || name == fd2_path);
|
|
|
|
}
|
|
|
|
cnt++;
|
|
|
|
}).ensure();
|
2018-09-11 21:17:01 +02:00
|
|
|
}
|
2019-05-01 11:23:19 +02:00
|
|
|
ASSERT_EQ((5 * 2 + 2) * ITER_COUNT, cnt);
|
|
|
|
bool was_abort = false;
|
2019-09-28 04:14:21 +02:00
|
|
|
walk_path(main_dir, [&](CSlice name, WalkPath::Type type) {
|
|
|
|
CHECK(!was_abort);
|
|
|
|
if (type == WalkPath::Type::EnterDir && ends_with(name, PSLICE() << TD_DIR_SLASH << "B")) {
|
|
|
|
was_abort = true;
|
|
|
|
return WalkPath::Action::Abort;
|
|
|
|
}
|
|
|
|
return WalkPath::Action::Continue;
|
|
|
|
}).ensure();
|
2019-05-01 11:23:19 +02:00
|
|
|
CHECK(was_abort);
|
|
|
|
|
|
|
|
cnt = 0;
|
|
|
|
bool is_first_dir = true;
|
2019-09-28 04:14:21 +02:00
|
|
|
walk_path(main_dir, [&](CSlice name, WalkPath::Type type) {
|
|
|
|
cnt++;
|
|
|
|
if (type == WalkPath::Type::EnterDir) {
|
|
|
|
if (is_first_dir) {
|
|
|
|
is_first_dir = false;
|
|
|
|
} else {
|
|
|
|
return WalkPath::Action::SkipDir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return WalkPath::Action::Continue;
|
|
|
|
}).ensure();
|
2019-05-01 11:23:19 +02:00
|
|
|
ASSERT_EQ(6, cnt);
|
2018-09-11 21:17:01 +02:00
|
|
|
|
2019-06-19 16:31:25 +02:00
|
|
|
ASSERT_EQ(0u, fd.get_size().move_as_ok());
|
2018-08-13 19:15:09 +02:00
|
|
|
ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok());
|
|
|
|
ASSERT_EQ(4u, fd.pwrite("abcd", 1).move_as_ok());
|
|
|
|
char buf[100];
|
|
|
|
MutableSlice buf_slice(buf, sizeof(buf));
|
|
|
|
ASSERT_TRUE(fd.pread(buf_slice.substr(0, 4), 2).is_error());
|
|
|
|
fd.seek(11).ensure();
|
|
|
|
ASSERT_EQ(2u, fd.write("?!").move_as_ok());
|
|
|
|
|
|
|
|
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Read | FileFd::CreateNew).is_error());
|
|
|
|
fd = FileFd::open(fd_path, FileFd::Read | FileFd::Create).move_as_ok();
|
2019-06-19 16:31:25 +02:00
|
|
|
ASSERT_EQ(13u, fd.get_size().move_as_ok());
|
2018-08-13 19:15:09 +02:00
|
|
|
ASSERT_EQ(4u, fd.pread(buf_slice.substr(0, 4), 1).move_as_ok());
|
|
|
|
ASSERT_STREQ("abcd", buf_slice.substr(0, 4));
|
|
|
|
|
|
|
|
fd.seek(0).ensure();
|
|
|
|
ASSERT_EQ(13u, fd.read(buf_slice.substr(0, 13)).move_as_ok());
|
|
|
|
ASSERT_STREQ("Habcd world?!", buf_slice.substr(0, 13));
|
|
|
|
}
|
2019-07-06 13:29:15 +02:00
|
|
|
|
2020-01-02 23:01:39 +01:00
|
|
|
TEST(Port, SparseFiles) {
|
|
|
|
CSlice path = "sparse.txt";
|
|
|
|
unlink(path).ignore();
|
|
|
|
auto fd = FileFd::open(path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
|
|
|
ASSERT_EQ(0, fd.get_size().move_as_ok());
|
|
|
|
int64 offset = 100000000;
|
2020-01-03 17:37:03 +01:00
|
|
|
fd.pwrite("a", offset).ensure();
|
2020-01-02 23:01:39 +01:00
|
|
|
ASSERT_EQ(offset + 1, fd.get_size().move_as_ok());
|
|
|
|
auto real_size = fd.get_real_size().move_as_ok();
|
2020-02-19 02:55:25 +01:00
|
|
|
if (real_size >= offset + 1) {
|
2020-01-02 23:01:39 +01:00
|
|
|
LOG(ERROR) << "File system doesn't support sparse files, rewind during streaming can be slow";
|
|
|
|
}
|
|
|
|
unlink(path).ensure();
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:29:15 +02:00
|
|
|
TEST(Port, Writev) {
|
|
|
|
std::vector<IoSlice> vec;
|
|
|
|
CSlice test_file_path = "test.txt";
|
|
|
|
unlink(test_file_path).ignore();
|
|
|
|
auto fd = FileFd::open(test_file_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
|
|
|
|
vec.push_back(as_io_slice("a"));
|
|
|
|
vec.push_back(as_io_slice("b"));
|
|
|
|
vec.push_back(as_io_slice("cd"));
|
|
|
|
ASSERT_EQ(4u, fd.writev(vec).move_as_ok());
|
|
|
|
vec.clear();
|
|
|
|
vec.push_back(as_io_slice("efg"));
|
|
|
|
vec.push_back(as_io_slice(""));
|
|
|
|
vec.push_back(as_io_slice("hi"));
|
|
|
|
ASSERT_EQ(5u, fd.writev(vec).move_as_ok());
|
|
|
|
fd.close();
|
|
|
|
fd = FileFd::open(test_file_path, FileFd::Read).move_as_ok();
|
|
|
|
Slice expected_content = "abcdefghi";
|
|
|
|
ASSERT_EQ(static_cast<int64>(expected_content.size()), fd.get_size().ok());
|
|
|
|
std::string content(expected_content.size(), '\0');
|
|
|
|
ASSERT_EQ(content.size(), fd.read(content).move_as_ok());
|
|
|
|
ASSERT_EQ(expected_content, content);
|
|
|
|
}
|
|
|
|
|
2019-07-21 20:07:07 +02:00
|
|
|
#if TD_PORT_POSIX && !TD_THREAD_UNSUPPORTED
|
2019-07-06 13:29:15 +02:00
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <unistd.h>
|
2019-07-21 20:07:07 +02:00
|
|
|
|
|
|
|
#include <algorithm>
|
2019-07-06 13:29:15 +02:00
|
|
|
#include <mutex>
|
|
|
|
|
2019-07-21 20:07:07 +02:00
|
|
|
static std::mutex m;
|
|
|
|
static std::vector<std::string> ptrs;
|
|
|
|
static std::vector<int *> addrs;
|
|
|
|
static TD_THREAD_LOCAL int thread_id;
|
|
|
|
|
|
|
|
static void on_user_signal(int sig) {
|
2019-07-06 13:29:15 +02:00
|
|
|
int addr;
|
|
|
|
addrs[thread_id] = &addr;
|
|
|
|
char ptr[10];
|
|
|
|
snprintf(ptr, 6, "%d", thread_id);
|
|
|
|
std::unique_lock<std::mutex> guard(m);
|
|
|
|
ptrs.push_back(std::string(ptr));
|
|
|
|
}
|
|
|
|
|
2020-08-07 17:50:33 +02:00
|
|
|
TEST(Port, SignalsAndThread) {
|
2019-07-06 13:29:15 +02:00
|
|
|
setup_signals_alt_stack().ensure();
|
|
|
|
set_signal_handler(SignalType::User, on_user_signal).ensure();
|
2020-08-07 17:50:33 +02:00
|
|
|
SCOPE_EXIT {
|
|
|
|
set_signal_handler(SignalType::User, nullptr).ensure();
|
|
|
|
};
|
2019-07-06 13:29:15 +02:00
|
|
|
std::vector<std::string> ans = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
|
|
|
|
{
|
|
|
|
std::vector<td::thread> threads;
|
|
|
|
int thread_n = 10;
|
|
|
|
std::vector<Stage> stages(thread_n);
|
|
|
|
ptrs.clear();
|
|
|
|
addrs.resize(thread_n);
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
threads.emplace_back([&, i] {
|
|
|
|
setup_signals_alt_stack().ensure();
|
|
|
|
if (i != 0) {
|
|
|
|
stages[i].wait(2);
|
|
|
|
}
|
|
|
|
thread_id = i;
|
|
|
|
pthread_kill(pthread_self(), SIGUSR1);
|
|
|
|
if (i + 1 < thread_n) {
|
|
|
|
stages[i + 1].wait(2);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for (auto &t : threads) {
|
|
|
|
t.join();
|
|
|
|
}
|
|
|
|
CHECK(ptrs == ans);
|
|
|
|
|
2020-08-02 00:40:48 +02:00
|
|
|
//LOG(ERROR) << ptrs;
|
2019-07-06 13:29:15 +02:00
|
|
|
//LOG(ERROR) << std::set<int *>(addrs.begin(), addrs.end()).size();
|
|
|
|
//LOG(ERROR) << addrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Stage stage;
|
|
|
|
std::vector<td::thread> threads;
|
|
|
|
int thread_n = 10;
|
|
|
|
ptrs.clear();
|
|
|
|
addrs.resize(thread_n);
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
threads.emplace_back([&, i] {
|
|
|
|
stage.wait(thread_n);
|
|
|
|
thread_id = i;
|
|
|
|
pthread_kill(pthread_self(), SIGUSR1);
|
|
|
|
//kill(pid_t(syscall(SYS_gettid)), SIGUSR1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for (auto &t : threads) {
|
|
|
|
t.join();
|
|
|
|
}
|
|
|
|
std::sort(ptrs.begin(), ptrs.end());
|
|
|
|
CHECK(ptrs == ans);
|
2019-07-23 02:53:46 +02:00
|
|
|
std::sort(addrs.begin(), addrs.end());
|
|
|
|
ASSERT_TRUE(std::unique(addrs.begin(), addrs.end()) == addrs.end());
|
2019-07-06 13:29:15 +02:00
|
|
|
//LOG(ERROR) << addrs;
|
|
|
|
}
|
|
|
|
}
|
2020-08-07 17:50:33 +02:00
|
|
|
TEST(Port, EventFdAndSignals) {
|
|
|
|
set_signal_handler(SignalType::User, [](int signal) {}).ensure();
|
|
|
|
SCOPE_EXIT {
|
|
|
|
set_signal_handler(SignalType::User, nullptr).ensure();
|
|
|
|
};
|
|
|
|
|
|
|
|
std::atomic_flag flag;
|
|
|
|
flag.test_and_set();
|
|
|
|
auto main_thread = pthread_self();
|
|
|
|
td::thread interrupt_thread{[&flag, &main_thread] {
|
|
|
|
setup_signals_alt_stack().ensure();
|
|
|
|
while (flag.test_and_set()) {
|
|
|
|
pthread_kill(main_thread, SIGUSR1);
|
|
|
|
td::usleep_for(1000 * td::Random::fast(1, 10)); // 0.001s - 0.01s
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
|
|
|
|
for (int timeout_ms : {0, 1, 2, 10, 100, 500}) {
|
|
|
|
double min_diff = 10000000;
|
|
|
|
double max_diff = 0;
|
|
|
|
for (int t = 0; t < max(5, 1000 / max(timeout_ms, 1)); t++) {
|
|
|
|
td::EventFd event_fd;
|
|
|
|
event_fd.init();
|
|
|
|
auto start = td::Timestamp::now();
|
|
|
|
event_fd.wait(timeout_ms);
|
|
|
|
auto end = td::Timestamp::now();
|
|
|
|
auto passed = end.at() - start.at();
|
|
|
|
auto diff = passed * 1000 - timeout_ms;
|
|
|
|
min_diff = min(min_diff, diff);
|
|
|
|
max_diff = max(max_diff, diff);
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_CHECK(min_diff >= 0) << min_diff;
|
|
|
|
LOG_CHECK(max_diff < 10) << max_diff;
|
|
|
|
LOG(ERROR) << min_diff << " " << max_diff;
|
|
|
|
}
|
|
|
|
flag.clear();
|
|
|
|
}
|
2019-07-06 13:29:15 +02:00
|
|
|
#endif
|