1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

View file

@ -0,0 +1,35 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/Clocks.h"
#include <chrono>
namespace td {
ClocksDefault::Duration ClocksDefault::monotonic() {
auto duration = std::chrono::steady_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
ClocksDefault::Duration ClocksDefault::system() {
auto duration = std::chrono::system_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
} // namespace td

View file

@ -0,0 +1,40 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
namespace td {
class ClocksBase {
public:
using Duration = double;
};
// TODO: (maybe) write system specific functions.
class ClocksDefault {
public:
using Duration = ClocksBase::Duration;
static Duration monotonic();
static Duration system();
};
using Clocks = ClocksDefault;
} // namespace td

View file

@ -0,0 +1,159 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#undef small
#if TD_WINRT
#include "td/utils/port/wstring_convert.h"
#include "collection.h"
#include <cstdint>
#include <map>
#include <mutex>
#define REF_NEW ref new
#define CLRCALL
#define DEPRECATED_ATTRIBUTE(message) ::Windows::Foundation::Metadata::Deprecated(message,\
::Windows::Foundation::Metadata::DeprecationType::Deprecate, 0x0)
namespace CxCli {
using Windows::Foundation::Collections::IVector;
#define Array IVector
using Platform::Collections::Vector;
#define ArraySize(arr) ((arr)->Size)
#define ArrayGet(arr, index) ((arr)->GetAt(index))
#define ArraySet(arr, index, value) ((arr)->SetAt((index), (value)))
#define ArrayIndexType unsigned
using Platform::String;
using Platform::NullReferenceException;
template <class Key, class Value> class ConcurrentDictionary {
public:
bool TryGetValue(Key key, Value &value) {
std::lock_guard<std::mutex> guard(mutex_);
auto it = impl_.find(key);
if (it == impl_.end()) {
return false;
}
value = it->second;
return true;
}
bool TryRemove(Key key, Value &value) {
std::lock_guard<std::mutex> guard(mutex_);
auto it = impl_.find(key);
if (it == impl_.end()) {
return false;
}
value = std::move(it->second);
impl_.erase(it);
return true;
}
Value &operator [] (Key key) {
std::lock_guard<std::mutex> guard(mutex_);
return impl_[key];
}
private:
std::mutex mutex_;
std::map<Key, Value> impl_;
};
inline std::int64_t Increment(volatile std::int64_t &value) {
return InterlockedIncrement64(&value);
}
inline std::string string_to_unmanaged(String^ str) {
if (!str) {
return std::string();
}
return td::from_wstring(str->Data(), str->Length()).ok();
}
inline String^ string_from_unmanaged(const std::string &from) {
auto tmp = td::to_wstring(from).ok();
return REF_NEW String(tmp.c_str(), static_cast<unsigned>(tmp.size()));
}
} // namespace CxCli
#elif TD_CLI
#define REF_NEW gcnew
#define CLRCALL __clrcall
#define DEPRECATED_ATTRIBUTE(message) System::ObsoleteAttribute(message)
namespace CxCli {
using uint8 = td::uint8;
using int32 = td::int32;
using int64 = td::int64;
using float64 = double;
#define Array array
#define Vector array
#define ArraySize(arr) ((arr)->Length)
#define ArrayGet(arr, index) ((arr)[index])
#define ArraySet(arr, index, value) ((arr)[index] = (value))
#define ArrayIndexType int
using System::String;
using System::NullReferenceException;
using System::Collections::Concurrent::ConcurrentDictionary;
inline std::int64_t Increment(std::int64_t %value) {
return System::Threading::Interlocked::Increment(value);
}
inline std::string string_to_unmanaged(String^ str) {
if (!str || str->Length == 0) {
return std::string();
}
Array<System::Byte>^ bytes = System::Text::Encoding::UTF8->GetBytes(str);
cli::pin_ptr<System::Byte> pinned_ptr = &bytes[0];
std::string result(reinterpret_cast<const char *>(&pinned_ptr[0]), bytes->Length);
return result;
}
inline String^ string_from_unmanaged(const std::string &from) {
if (from.empty()) {
return String::Empty;
}
Array<System::Byte>^ bytes = REF_NEW Vector<System::Byte>(static_cast<ArrayIndexType>(from.size()));
cli::pin_ptr<System::Byte> pinned_ptr = &bytes[0];
for (size_t i = 0; i < from.size(); ++i) {
pinned_ptr[i] = from[i];
}
return System::Text::Encoding::UTF8->GetString(bytes);
}
} // namespace CxCli
#endif

View file

@ -0,0 +1,45 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
// include all and let config.h decide
#include "td/utils/port/detail/EventFdBsd.h"
#include "td/utils/port/detail/EventFdLinux.h"
#include "td/utils/port/detail/EventFdWindows.h"
namespace td {
// clang-format off
#if TD_EVENTFD_LINUX
using EventFd = detail::EventFdLinux;
#elif TD_EVENTFD_BSD
using EventFd = detail::EventFdBsd;
#elif TD_EVENTFD_WINDOWS
using EventFd = detail::EventFdWindows;
#elif TD_EVENTFD_UNSUPPORTED
#else
#error "EventFd's implementation is not defined"
#endif
// clang-format on
} // namespace td

View file

@ -0,0 +1,44 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/Status.h"
namespace td {
class EventFdBase {
public:
EventFdBase() = default;
EventFdBase(const EventFdBase &) = delete;
EventFdBase &operator=(const EventFdBase &) = delete;
EventFdBase(EventFdBase &&) = default;
EventFdBase &operator=(EventFdBase &&) = default;
virtual ~EventFdBase() = default;
virtual void init() = 0;
virtual bool empty() = 0;
virtual void close() = 0;
virtual PollableFdInfo &get_poll_info() = 0;
virtual Status get_pending_error() TD_WARN_UNUSED_RESULT = 0;
virtual void release() = 0;
virtual void acquire() = 0;
virtual void wait(int timeout_ms) = 0;
};
} // namespace td

View file

@ -0,0 +1,587 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/FileFd.h"
#if TD_PORT_WINDOWS
#include "td/utils/port/Stat.h"
#include "td/utils/port/wstring_convert.h"
#endif
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/sleep.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/StringBuilder.h"
#include <cstring>
#include <mutex>
#include <unordered_set>
#if TD_PORT_POSIX
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#endif
namespace td {
namespace {
struct PrintFlags {
int32 flags;
};
StringBuilder &operator<<(StringBuilder &sb, const PrintFlags &print_flags) {
auto flags = print_flags.flags;
if (flags & ~(FileFd::Write | FileFd::Read | FileFd::Truncate | FileFd::Create | FileFd::Append | FileFd::CreateNew |
FileFd::Direct | FileFd::WinStat)) {
return sb << "opened with invalid flags " << flags;
}
if (flags & FileFd::Create) {
sb << "opened/created ";
} else if (flags & FileFd::CreateNew) {
sb << "created ";
} else {
sb << "opened ";
}
if ((flags & FileFd::Write) && (flags & FileFd::Read)) {
if (flags & FileFd::Append) {
sb << "for reading and appending";
} else {
sb << "for reading and writing";
}
} else if (flags & FileFd::Write) {
if (flags & FileFd::Append) {
sb << "for appending";
} else {
sb << "for writing";
}
} else if (flags & FileFd::Read) {
sb << "for reading";
} else {
sb << "for nothing";
}
if (flags & FileFd::Truncate) {
sb << " with truncation";
}
if (flags & FileFd::Direct) {
sb << " for direct io";
}
if (flags & FileFd::WinStat) {
sb << " for stat";
}
return sb;
}
} // namespace
namespace detail {
class FileFdImpl {
public:
PollableFdInfo info;
};
} // namespace detail
FileFd::FileFd() = default;
FileFd::FileFd(FileFd &&) = default;
FileFd &FileFd::operator=(FileFd &&) = default;
FileFd::~FileFd() = default;
FileFd::FileFd(unique_ptr<detail::FileFdImpl> impl) : impl_(std::move(impl)) {
}
Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
if (flags & ~(Write | Read | Truncate | Create | Append | CreateNew | Direct | WinStat)) {
return Status::Error(PSLICE() << "File \"" << filepath << "\" has failed to be " << PrintFlags{flags});
}
if ((flags & (Write | Read)) == 0) {
return Status::Error(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
#if TD_PORT_POSIX
int native_flags = 0;
if ((flags & Write) && (flags & Read)) {
native_flags |= O_RDWR;
} else if (flags & Write) {
native_flags |= O_WRONLY;
} else {
CHECK(flags & Read);
native_flags |= O_RDONLY;
}
if (flags & Truncate) {
native_flags |= O_TRUNC;
}
if (flags & Create) {
native_flags |= O_CREAT;
} else if (flags & CreateNew) {
native_flags |= O_CREAT;
native_flags |= O_EXCL;
}
if (flags & Append) {
native_flags |= O_APPEND;
}
#if TD_LINUX
if (flags & Direct) {
native_flags |= O_DIRECT;
}
#endif
int native_fd = detail::skip_eintr([&] { return ::open(filepath.c_str(), native_flags, static_cast<mode_t>(mode)); });
if (native_fd < 0) {
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
return from_native_fd(NativeFd(native_fd));
#elif TD_PORT_WINDOWS
// TODO: support modes
auto r_filepath = to_wstring(filepath);
if (r_filepath.is_error()) {
return Status::Error(PSLICE() << "Failed to convert file path \" << filepath << \" to UTF-16");
}
auto w_filepath = r_filepath.move_as_ok();
DWORD desired_access = 0;
if ((flags & Write) && (flags & Read)) {
desired_access |= GENERIC_READ | GENERIC_WRITE;
} else if (flags & Write) {
desired_access |= GENERIC_WRITE;
} else {
CHECK(flags & Read);
desired_access |= GENERIC_READ;
}
// TODO: share mode
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE;
DWORD creation_disposition = 0;
if (flags & Create) {
if (flags & Truncate) {
creation_disposition = CREATE_ALWAYS;
} else {
creation_disposition = OPEN_ALWAYS;
}
} else if (flags & CreateNew) {
creation_disposition = CREATE_NEW;
} else {
if (flags & Truncate) {
creation_disposition = TRUNCATE_EXISTING;
} else {
creation_disposition = OPEN_EXISTING;
}
}
DWORD native_flags = 0;
if (flags & Direct) {
native_flags |= FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING;
}
if (flags & WinStat) {
native_flags |= FILE_FLAG_BACKUP_SEMANTICS;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
auto handle =
CreateFile(w_filepath.c_str(), desired_access, share_mode, nullptr, creation_disposition, native_flags, nullptr);
#else
CREATEFILE2_EXTENDED_PARAMETERS extended_parameters;
std::memset(&extended_parameters, 0, sizeof(extended_parameters));
extended_parameters.dwSize = sizeof(extended_parameters);
extended_parameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
extended_parameters.dwFileFlags = native_flags;
auto handle = CreateFile2(w_filepath.c_str(), desired_access, share_mode, creation_disposition, &extended_parameters);
#endif
if (handle == INVALID_HANDLE_VALUE) {
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
auto native_fd = NativeFd(handle);
if (flags & Append) {
LARGE_INTEGER offset;
offset.QuadPart = 0;
auto set_pointer_res = SetFilePointerEx(handle, offset, nullptr, FILE_END);
if (!set_pointer_res) {
return OS_ERROR(PSLICE() << "Failed to seek to the end of file \"" << filepath << "\"");
}
}
return from_native_fd(std::move(native_fd));
#endif
}
FileFd FileFd::from_native_fd(NativeFd native_fd) {
auto impl = make_unique<detail::FileFdImpl>();
impl->info.set_native_fd(std::move(native_fd));
impl->info.add_flags(PollFlags::Write());
return FileFd(std::move(impl));
}
Result<size_t> FileFd::write(Slice slice) {
auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
auto bytes_written = detail::skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
bool success = bytes_written >= 0;
#elif TD_PORT_WINDOWS
DWORD bytes_written = 0;
BOOL success = WriteFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, nullptr);
#endif
if (success) {
return narrow_cast<size_t>(bytes_written);
}
return OS_ERROR(PSLICE() << "Write to " << get_native_fd() << " has failed");
}
Result<size_t> FileFd::writev(Span<IoSlice> slices) {
#if TD_PORT_POSIX
auto native_fd = get_native_fd().fd();
TRY_RESULT(slices_size, narrow_cast_safe<int>(slices.size()));
auto bytes_written = detail::skip_eintr([&] { return ::writev(native_fd, slices.begin(), slices_size); });
bool success = bytes_written >= 0;
if (success) {
return narrow_cast<size_t>(bytes_written);
}
return OS_ERROR(PSLICE() << "Writev to " << get_native_fd() << " has failed");
#else
size_t res = 0;
for (auto slice : slices) {
TRY_RESULT(size, write(slice));
res += size;
}
return res;
#endif
}
Result<size_t> FileFd::read(MutableSlice slice) {
auto native_fd = get_native_fd().fd();
#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;
BOOL success = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, nullptr);
bool is_eof = bytes_read == 0;
#endif
if (success) {
if (is_eof) {
get_poll_info().clear_flags(PollFlags::Read());
}
return static_cast<size_t>(bytes_read);
}
return OS_ERROR(PSLICE() << "Read from " << get_native_fd() << " has failed");
}
Result<size_t> FileFd::pwrite(Slice slice, int64 offset) {
if (offset < 0) {
return Status::Error("Offset must be non-negative");
}
auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
TRY_RESULT(offset_off_t, narrow_cast_safe<off_t>(offset));
auto bytes_written =
detail::skip_eintr([&] { return ::pwrite(native_fd, slice.begin(), slice.size(), offset_off_t); });
bool success = bytes_written >= 0;
#elif TD_PORT_WINDOWS
DWORD bytes_written = 0;
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
BOOL success = WriteFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, &overlapped);
#endif
if (success) {
return narrow_cast<size_t>(bytes_written);
}
return OS_ERROR(PSLICE() << "Pwrite to " << get_native_fd() << " at offset " << offset << " has failed");
}
Result<size_t> FileFd::pread(MutableSlice slice, int64 offset) const {
if (offset < 0) {
return Status::Error("Offset must be non-negative");
}
auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
TRY_RESULT(offset_off_t, narrow_cast_safe<off_t>(offset));
auto bytes_read = detail::skip_eintr([&] { return ::pread(native_fd, slice.begin(), slice.size(), offset_off_t); });
bool success = bytes_read >= 0;
#elif TD_PORT_WINDOWS
DWORD bytes_read = 0;
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
BOOL success = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, &overlapped);
#endif
if (success) {
return narrow_cast<size_t>(bytes_read);
}
return OS_ERROR(PSLICE() << "Pread from " << get_native_fd() << " at offset " << offset << " has failed");
}
static std::mutex in_process_lock_mutex;
static std::unordered_set<string> locked_files;
static Status create_local_lock(const string &path, int32 &max_tries) {
while (true) {
{ // mutex lock scope
std::lock_guard<std::mutex> lock(in_process_lock_mutex);
if (locked_files.find(path) == locked_files.end()) {
VLOG(fd) << "Lock file \"" << path << '"';
locked_files.insert(path);
return Status::OK();
}
}
if (--max_tries <= 0) {
return Status::Error(
0, PSLICE() << "Can't lock file \"" << path << "\", because it is already in use by current program");
}
usleep_for(100000);
}
}
Status FileFd::lock(const LockFlags flags, const string &path, int32 max_tries) {
if (max_tries <= 0) {
return Status::Error(0, "Can't lock file: wrong max_tries");
}
bool need_local_unlock = false;
if (!path.empty()) {
if (flags == LockFlags::Unlock) {
need_local_unlock = true;
} else if (flags == LockFlags::Read) {
LOG(FATAL) << "Local locking in Read mode is unsupported";
} else {
CHECK(flags == LockFlags::Write);
VLOG(fd) << "Trying to lock file \"" << path << '"';
TRY_STATUS(create_local_lock(path, max_tries));
need_local_unlock = true;
}
}
SCOPE_EXIT {
if (need_local_unlock) {
remove_local_lock(path);
}
};
#if TD_PORT_POSIX
auto native_fd = get_native_fd().fd();
#elif TD_PORT_WINDOWS
auto native_fd = get_native_fd().fd();
#endif
while (true) {
#if TD_PORT_POSIX
struct flock lock;
std::memset(&lock, 0, sizeof(lock));
lock.l_type = static_cast<short>([&] {
switch (flags) {
case LockFlags::Read:
return F_RDLCK;
case LockFlags::Write:
return F_WRLCK;
case LockFlags::Unlock:
return F_UNLCK;
default:
UNREACHABLE();
return F_UNLCK;
}
}());
lock.l_whence = SEEK_SET;
if (fcntl(native_fd, F_SETLK, &lock) == -1) {
if (errno == EAGAIN) {
#elif TD_PORT_WINDOWS
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
BOOL result;
if (flags == LockFlags::Unlock) {
result = UnlockFileEx(native_fd, 0, MAXDWORD, MAXDWORD, &overlapped);
} else {
DWORD dw_flags = LOCKFILE_FAIL_IMMEDIATELY;
if (flags == LockFlags::Write) {
dw_flags |= LOCKFILE_EXCLUSIVE_LOCK;
}
result = LockFileEx(native_fd, dw_flags, 0, MAXDWORD, MAXDWORD, &overlapped);
}
if (!result) {
if (GetLastError() == ERROR_LOCK_VIOLATION) {
#endif
if (--max_tries > 0) {
usleep_for(100000);
continue;
}
return OS_ERROR(PSLICE() << "Can't lock file \"" << path
<< "\", because it is already in use; check for another program instance running");
}
return OS_ERROR("Can't lock file");
}
break;
}
if (flags == LockFlags::Write) {
need_local_unlock = false;
}
return Status::OK();
}
void FileFd::remove_local_lock(const string &path) {
if (!path.empty()) {
VLOG(fd) << "Unlock file \"" << path << '"';
std::unique_lock<std::mutex> lock(in_process_lock_mutex);
auto erased = locked_files.erase(path);
CHECK(erased > 0);
}
}
void FileFd::close() {
impl_.reset();
}
bool FileFd::empty() const {
return !impl_;
}
const NativeFd &FileFd::get_native_fd() const {
return get_poll_info().native_fd();
}
NativeFd FileFd::move_as_native_fd() {
auto res = get_poll_info().move_as_native_fd();
impl_.reset();
return res;
}
Result<int64> FileFd::get_size() const {
TRY_RESULT(s, stat());
return s.size_;
}
#if TD_PORT_WINDOWS
static uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
const auto FILETIME_UNIX_TIME_DIFF = 116444736000000000ll;
return static_cast<uint64>((filetime - FILETIME_UNIX_TIME_DIFF) * 100);
}
#endif
Result<Stat> FileFd::stat() const {
CHECK(!empty());
#if TD_PORT_POSIX
return detail::fstat(get_native_fd().fd());
#elif TD_PORT_WINDOWS
Stat res;
FILE_BASIC_INFO basic_info;
auto status = GetFileInformationByHandleEx(get_native_fd().fd(), FileBasicInfo, &basic_info, sizeof(basic_info));
if (!status) {
return OS_ERROR("Get FileBasicInfo failed");
}
res.atime_nsec_ = filetime_to_unix_time_nsec(basic_info.LastAccessTime.QuadPart);
res.mtime_nsec_ = filetime_to_unix_time_nsec(basic_info.LastWriteTime.QuadPart);
res.is_dir_ = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
res.is_reg_ = !res.is_dir_; // TODO this is still wrong
FILE_STANDARD_INFO standard_info;
status = GetFileInformationByHandleEx(get_native_fd().fd(), FileStandardInfo, &standard_info, sizeof(standard_info));
if (!status) {
return OS_ERROR("Get FileStandardInfo failed");
}
res.size_ = standard_info.EndOfFile.QuadPart;
return res;
#endif
}
Status FileFd::sync() {
CHECK(!empty());
#if TD_PORT_POSIX
#if TD_DARWIN
if (detail::skip_eintr([&] { return fcntl(get_native_fd().fd(), F_FULLFSYNC); }) == -1) {
#else
if (detail::skip_eintr([&] { return fsync(get_native_fd().fd()); }) != 0) {
#endif
#elif TD_PORT_WINDOWS
if (FlushFileBuffers(get_native_fd().fd()) == 0) {
#endif
return OS_ERROR("Sync failed");
}
return Status::OK();
}
Status FileFd::seek(int64 position) {
CHECK(!empty());
#if TD_PORT_POSIX
TRY_RESULT(position_off_t, narrow_cast_safe<off_t>(position));
if (detail::skip_eintr([&] { return ::lseek(get_native_fd().fd(), position_off_t, SEEK_SET); }) < 0) {
#elif TD_PORT_WINDOWS
LARGE_INTEGER offset;
offset.QuadPart = position;
if (SetFilePointerEx(get_native_fd().fd(), offset, nullptr, FILE_BEGIN) == 0) {
#endif
return OS_ERROR("Seek failed");
}
return Status::OK();
}
Status FileFd::truncate_to_current_position(int64 current_position) {
CHECK(!empty());
#if TD_PORT_POSIX
TRY_RESULT(current_position_off_t, narrow_cast_safe<off_t>(current_position));
if (detail::skip_eintr([&] { return ::ftruncate(get_native_fd().fd(), current_position_off_t); }) < 0) {
#elif TD_PORT_WINDOWS
if (SetEndOfFile(get_native_fd().fd()) == 0) {
#endif
return OS_ERROR("Truncate failed");
}
return Status::OK();
}
PollableFdInfo &FileFd::get_poll_info() {
CHECK(!empty());
return impl_->info;
}
const PollableFdInfo &FileFd::get_poll_info() const {
CHECK(!empty());
return impl_->info;
}
} // namespace td

View file

@ -0,0 +1,87 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/IoSlice.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class FileFdImpl;
}
class FileFd {
public:
FileFd();
FileFd(FileFd &&);
FileFd &operator=(FileFd &&);
~FileFd();
FileFd(const FileFd &) = delete;
FileFd &operator=(const FileFd &) = delete;
enum Flags : int32 { Write = 1, Read = 2, Truncate = 4, Create = 8, Append = 16, CreateNew = 32, Direct = 64 };
enum PrivateFlags : int32 { WinStat = 128 };
static Result<FileFd> open(CSlice filepath, int32 flags, int32 mode = 0600) TD_WARN_UNUSED_RESULT;
static FileFd from_native_fd(NativeFd fd) TD_WARN_UNUSED_RESULT;
Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT;
Result<size_t> writev(Span<IoSlice> slices) TD_WARN_UNUSED_RESULT;
Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
Result<size_t> pwrite(Slice slice, int64 offset) TD_WARN_UNUSED_RESULT;
Result<size_t> pread(MutableSlice slice, int64 offset) const TD_WARN_UNUSED_RESULT;
enum class LockFlags { Write, Read, Unlock };
Status lock(const LockFlags flags, const string &path, int32 max_tries) TD_WARN_UNUSED_RESULT;
static void remove_local_lock(const string &path);
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
void close();
bool empty() const;
Result<int64> get_size() const;
Result<Stat> stat() const;
Status sync() TD_WARN_UNUSED_RESULT;
Status seek(int64 position) TD_WARN_UNUSED_RESULT;
Status truncate_to_current_position(int64 current_position) TD_WARN_UNUSED_RESULT;
const NativeFd &get_native_fd() const;
NativeFd move_as_native_fd();
private:
unique_ptr<detail::FileFdImpl> impl_;
explicit FileFd(unique_ptr<detail::FileFdImpl> impl);
};
} // namespace td

View file

@ -0,0 +1,622 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#define _WINSOCK_DEPRECATED_NO_WARNINGS // we need to use inet_addr instead of inet_pton
#include "td/utils/port/IPAddress.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include "td/utils/utf8.h"
#if TD_WINDOWS
#include "td/utils/port/wstring_convert.h"
#else
#include <netdb.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#endif
#include <cstring>
#include <limits>
namespace td {
static bool is_ascii_host_char(char c) {
return static_cast<unsigned char>(c) <= 127;
}
static bool is_ascii_host(Slice host) {
for (auto c : host) {
if (!is_ascii_host_char(c)) {
return false;
}
}
return true;
}
#if !TD_WINDOWS
static void punycode(string &result, Slice part) {
vector<uint32> codes;
codes.reserve(utf8_length(part));
uint32 processed = 0;
auto begin = part.ubegin();
auto end = part.uend();
while (begin != end) {
uint32 code;
begin = next_utf8_unsafe(begin, &code, "punycode");
if (code <= 127u) {
result += to_lower(static_cast<char>(code));
processed++;
}
codes.push_back(code);
}
if (processed > 0) {
result += '-';
}
uint32 n = 127;
uint32 delta = 0;
int bias = -72;
bool is_first = true;
while (processed < codes.size()) {
// choose lowest not processed code
uint32 next_n = 0x110000;
for (auto code : codes) {
if (code > n && code < next_n) {
next_n = code;
}
}
delta += (next_n - n - 1) * (processed + 1);
for (auto code : codes) {
if (code < next_n) {
delta++;
}
if (code == next_n) {
// found next symbol, encode delta
int left = static_cast<int>(delta);
while (true) {
bias += 36;
auto t = clamp(bias, 1, 26);
if (left < t) {
result += static_cast<char>(left + 'a');
break;
}
left -= t;
auto digit = t + left % (36 - t);
result += static_cast<char>(digit < 26 ? digit + 'a' : digit - 26 + '0');
left /= 36 - t;
}
processed++;
// update bias
if (is_first) {
delta /= 700;
is_first = false;
} else {
delta /= 2;
}
delta += delta / processed;
bias = 0;
while (delta > 35 * 13) {
delta /= 35;
bias -= 36;
}
bias -= static_cast<int>(36 * delta / (delta + 38));
delta = 0;
}
}
delta++;
n = next_n;
}
}
#endif
Result<string> idn_to_ascii(CSlice host) {
if (is_ascii_host(host)) {
return to_lower(host);
}
if (!check_utf8(host)) {
return Status::Error("Host name must be encoded in UTF-8");
}
const int MAX_DNS_NAME_LENGTH = 255;
if (host.size() >= MAX_DNS_NAME_LENGTH * 4) { // upper bound, 4 characters per symbol
return Status::Error("Host name is too long");
}
#if TD_WINDOWS
TRY_RESULT(whost, to_wstring(host));
wchar_t punycode[MAX_DNS_NAME_LENGTH + 1];
int result_length =
IdnToAscii(IDN_ALLOW_UNASSIGNED, whost.c_str(), narrow_cast<int>(whost.size()), punycode, MAX_DNS_NAME_LENGTH);
if (result_length == 0) {
return Status::Error("Host can't be converted to ASCII");
}
TRY_RESULT(idn_host, from_wstring(punycode, result_length));
return idn_host;
#else
auto parts = full_split(Slice(host), '.');
bool is_first = true;
string result;
result.reserve(host.size());
for (auto part : parts) {
if (!is_first) {
result += '.';
}
if (is_ascii_host(part)) {
result.append(part.data(), part.size());
} else {
// TODO nameprep should be applied first, but punycode is better than nothing.
// It is better to use libidn/ICU here if available
result += "xn--";
punycode(result, part);
}
is_first = false;
}
return result;
#endif
}
static CSlice get_ip_str(int family, const void *addr) {
const int buf_size = INET6_ADDRSTRLEN;
static TD_THREAD_LOCAL char *buf;
init_thread_local<char[]>(buf, buf_size);
const char *res = inet_ntop(family,
#if TD_WINDOWS
const_cast<PVOID>(addr),
#else
addr,
#endif
buf, buf_size);
if (res == nullptr) {
return CSlice();
} else {
return CSlice(res);
}
}
IPAddress::IPAddress() : is_valid_(false) {
}
bool IPAddress::is_valid() const {
return is_valid_;
}
bool IPAddress::is_reserved() const {
CHECK(is_valid());
if (is_ipv6()) {
// TODO proper check for reserved IPv6 addresses
return true;
}
uint32 ip = get_ipv4();
struct IpBlock {
CSlice ip;
int mask;
IpBlock(CSlice ip, int mask) : ip(ip), mask(mask) {
}
};
static const IpBlock blocks[] = {{"0.0.0.0", 8}, {"10.0.0.0", 8}, {"100.64.0.0", 10}, {"127.0.0.0", 8},
{"169.254.0.0", 16}, {"172.16.0.0", 12}, {"192.0.0.0", 24}, {"192.0.2.0", 24},
{"192.88.99.0", 24}, {"192.168.0.0", 16}, {"198.18.0.0", 15}, {"198.51.100.0", 24},
{"203.0.113.0", 24}, {"224.0.0.0", 3}};
for (auto &block : blocks) {
IPAddress block_ip_address;
block_ip_address.init_ipv4_port(block.ip, 80).ensure();
uint32 range = block_ip_address.get_ipv4();
CHECK(block.mask != 0);
uint32 mask = std::numeric_limits<uint32>::max() >> (32 - block.mask) << (32 - block.mask);
if ((ip & mask) == (range & mask)) {
return true;
}
}
return false;
}
const sockaddr *IPAddress::get_sockaddr() const {
CHECK(is_valid());
return &sockaddr_;
}
size_t IPAddress::get_sockaddr_len() const {
CHECK(is_valid());
switch (sockaddr_.sa_family) {
case AF_INET6:
return sizeof(ipv6_addr_);
case AF_INET:
return sizeof(ipv4_addr_);
default:
LOG(FATAL) << "Unknown address family";
return 0;
}
}
int IPAddress::get_address_family() const {
return get_sockaddr()->sa_family;
}
bool IPAddress::is_ipv4() const {
return is_valid() && get_address_family() == AF_INET;
}
bool IPAddress::is_ipv6() const {
return is_valid() && get_address_family() == AF_INET6;
}
uint32 IPAddress::get_ipv4() const {
CHECK(is_valid());
CHECK(is_ipv4());
return htonl(ipv4_addr_.sin_addr.s_addr);
}
Slice IPAddress::get_ipv6() const {
static_assert(sizeof(ipv6_addr_.sin6_addr) == 16, "ipv6 size == 16");
CHECK(is_valid());
CHECK(!is_ipv4());
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16);
}
IPAddress IPAddress::get_any_addr() const {
IPAddress res;
switch (get_address_family()) {
case AF_INET6:
res.init_ipv6_any();
break;
case AF_INET:
res.init_ipv4_any();
break;
default:
LOG(FATAL) << "Unknown address family";
}
return res;
}
void IPAddress::init_ipv4_any() {
is_valid_ = true;
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
ipv4_addr_.sin_family = AF_INET;
ipv4_addr_.sin_addr.s_addr = INADDR_ANY;
ipv4_addr_.sin_port = 0;
}
void IPAddress::init_ipv6_any() {
is_valid_ = true;
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
ipv6_addr_.sin6_family = AF_INET6;
ipv6_addr_.sin6_addr = in6addr_any;
ipv6_addr_.sin6_port = 0;
}
Status IPAddress::init_ipv6_port(CSlice ipv6, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
}
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
ipv6_addr_.sin6_family = AF_INET6;
ipv6_addr_.sin6_port = htons(static_cast<uint16>(port));
int err = inet_pton(AF_INET6, ipv6.c_str(), &ipv6_addr_.sin6_addr);
if (err == 0) {
return Status::Error(PSLICE() << "Failed inet_pton(AF_INET6, " << ipv6 << ")");
} else if (err == -1) {
return OS_SOCKET_ERROR(PSLICE() << "Failed inet_pton(AF_INET6, " << ipv6 << ")");
}
is_valid_ = true;
return Status::OK();
}
Status IPAddress::init_ipv6_as_ipv4_port(CSlice ipv4, int port) {
return init_ipv6_port(string("::FFFF:").append(ipv4.begin(), ipv4.size()), port);
}
Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
}
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
ipv4_addr_.sin_family = AF_INET;
ipv4_addr_.sin_port = htons(static_cast<uint16>(port));
int err = inet_pton(AF_INET, ipv4.c_str(), &ipv4_addr_.sin_addr);
if (err == 0) {
return Status::Error(PSLICE() << "Failed inet_pton(AF_INET, " << ipv4 << ")");
} else if (err == -1) {
return OS_SOCKET_ERROR(PSLICE() << "Failed inet_pton(AF_INET, " << ipv4 << ")");
}
is_valid_ = true;
return Status::OK();
}
Result<IPAddress> IPAddress::get_ipv4_address(CSlice host) {
// sometimes inet_addr allows much more valid IPv4 hosts than inet_pton,
// like 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001
auto ipv4_numeric_addr = inet_addr(host.c_str());
if (ipv4_numeric_addr == INADDR_NONE) {
return Status::Error("Host is not valid IPv4 address");
}
host = ::td::get_ip_str(AF_INET, &ipv4_numeric_addr);
IPAddress result;
auto status = result.init_ipv4_port(host, 1);
if (status.is_error()) {
return std::move(status);
}
return std::move(result);
}
Result<IPAddress> IPAddress::get_ipv6_address(CSlice host) {
IPAddress result;
auto status = result.init_ipv6_port(host, 1);
if (status.is_error()) {
return std::move(status);
}
return std::move(result);
}
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
return init_host_port(host, PSLICE() << port, prefer_ipv6);
}
Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
is_valid_ = false;
if (host.empty()) {
return Status::Error("Host is empty");
}
#if TD_WINDOWS
if (host == "..localmachine") {
return Status::Error("Host is invalid");
}
#endif
TRY_RESULT(ascii_host, idn_to_ascii(host));
host = ascii_host;
// some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form,
// and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses
auto ipv4_numeric_addr = inet_addr(host.c_str());
if (ipv4_numeric_addr != INADDR_NONE) {
host = ::td::get_ip_str(AF_INET, &ipv4_numeric_addr);
}
addrinfo hints;
addrinfo *info = nullptr;
std::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
LOG(DEBUG + 10) << "Try to init IP address of " << host << " with port " << port;
auto err = getaddrinfo(host.c_str(), port.c_str(), &hints, &info);
if (err != 0) {
#if TD_WINDOWS
return OS_SOCKET_ERROR("Failed to resolve host");
#else
return Status::Error(PSLICE() << "Failed to resolve host: " << gai_strerror(err));
#endif
}
SCOPE_EXIT {
freeaddrinfo(info);
};
addrinfo *best_info = nullptr;
for (auto *ptr = info; ptr != nullptr; ptr = ptr->ai_next) {
if (ptr->ai_family == AF_INET && (!prefer_ipv6 || best_info == nullptr)) {
// just use first IPv4 address if there is no IPv6 and it isn't preferred
best_info = ptr;
if (!prefer_ipv6) {
break;
}
}
if (ptr->ai_family == AF_INET6 && (prefer_ipv6 || best_info == nullptr)) {
// or first IPv6 address if there is no IPv4 and it isn't preferred
best_info = ptr;
if (prefer_ipv6) {
break;
}
}
}
if (best_info == nullptr) {
return Status::Error("Failed to find IPv4/IPv6 address");
}
return init_sockaddr(best_info->ai_addr, narrow_cast<socklen_t>(best_info->ai_addrlen));
}
Status IPAddress::init_host_port(CSlice host_port) {
auto pos = host_port.rfind(':');
if (pos == static_cast<size_t>(-1)) {
return Status::Error("Can't split string into host and port");
}
return init_host_port(host_port.substr(0, pos).str(), host_port.substr(pos + 1).str());
}
Status IPAddress::init_sockaddr(sockaddr *addr) {
if (addr->sa_family == AF_INET6) {
return init_sockaddr(addr, sizeof(ipv6_addr_));
} else if (addr->sa_family == AF_INET) {
return init_sockaddr(addr, sizeof(ipv4_addr_));
} else {
return init_sockaddr(addr, 0);
}
}
Status IPAddress::init_sockaddr(sockaddr *addr, socklen_t len) {
if (addr->sa_family == AF_INET6) {
CHECK(len == sizeof(ipv6_addr_));
std::memcpy(&ipv6_addr_, reinterpret_cast<sockaddr_in6 *>(addr), sizeof(ipv6_addr_));
} else if (addr->sa_family == AF_INET) {
CHECK(len == sizeof(ipv4_addr_));
std::memcpy(&ipv4_addr_, reinterpret_cast<sockaddr_in *>(addr), sizeof(ipv4_addr_));
} else {
return Status::Error(PSLICE() << "Unknown " << tag("sa_family", addr->sa_family));
}
is_valid_ = true;
LOG(DEBUG + 10) << "Have address " << get_ip_str() << " with port " << get_port();
return Status::OK();
}
Status IPAddress::init_socket_address(const SocketFd &socket_fd) {
is_valid_ = false;
auto socket = socket_fd.get_native_fd().socket();
socklen_t len = storage_size();
int ret = getsockname(socket, &sockaddr_, &len);
if (ret != 0) {
return OS_SOCKET_ERROR("Failed to get socket address");
}
is_valid_ = true;
return Status::OK();
}
Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
is_valid_ = false;
auto socket = socket_fd.get_native_fd().socket();
socklen_t len = storage_size();
int ret = getpeername(socket, &sockaddr_, &len);
if (ret != 0) {
return OS_SOCKET_ERROR("Failed to get peer socket address");
}
is_valid_ = true;
return Status::OK();
}
CSlice IPAddress::ipv4_to_str(uint32 ipv4) {
ipv4 = ntohl(ipv4);
return ::td::get_ip_str(AF_INET, &ipv4);
}
CSlice IPAddress::ipv6_to_str(Slice ipv6) {
CHECK(ipv6.size() == 16);
return ::td::get_ip_str(AF_INET6, ipv6.ubegin());
}
Slice IPAddress::get_ip_str() const {
if (!is_valid()) {
return Slice("0.0.0.0");
}
switch (get_address_family()) {
case AF_INET6:
return ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr);
case AF_INET:
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr);
default:
UNREACHABLE();
return Slice();
}
}
int IPAddress::get_port() const {
if (!is_valid()) {
return 0;
}
switch (get_address_family()) {
case AF_INET6:
return ntohs(ipv6_addr_.sin6_port);
case AF_INET:
return ntohs(ipv4_addr_.sin_port);
default:
UNREACHABLE();
return 0;
}
}
void IPAddress::set_port(int port) {
CHECK(is_valid());
switch (get_address_family()) {
case AF_INET6:
ipv6_addr_.sin6_port = htons(static_cast<uint16>(port));
break;
case AF_INET:
ipv4_addr_.sin_port = htons(static_cast<uint16>(port));
break;
default:
UNREACHABLE();
}
}
bool operator==(const IPAddress &a, const IPAddress &b) {
if (!a.is_valid() || !b.is_valid()) {
return !a.is_valid() && !b.is_valid();
}
if (a.get_address_family() != b.get_address_family()) {
return false;
}
if (a.get_address_family() == AF_INET) {
return a.ipv4_addr_.sin_port == b.ipv4_addr_.sin_port &&
std::memcmp(&a.ipv4_addr_.sin_addr, &b.ipv4_addr_.sin_addr, sizeof(a.ipv4_addr_.sin_addr)) == 0;
} else if (a.get_address_family() == AF_INET6) {
return a.ipv6_addr_.sin6_port == b.ipv6_addr_.sin6_port &&
std::memcmp(&a.ipv6_addr_.sin6_addr, &b.ipv6_addr_.sin6_addr, sizeof(a.ipv6_addr_.sin6_addr)) == 0;
}
LOG(FATAL) << "Unknown address family";
return false;
}
bool operator<(const IPAddress &a, const IPAddress &b) {
if (!a.is_valid() || !b.is_valid()) {
return !a.is_valid() && b.is_valid();
}
if (a.get_address_family() != b.get_address_family()) {
return a.get_address_family() < b.get_address_family();
}
if (a.get_address_family() == AF_INET) {
if (a.ipv4_addr_.sin_port != b.ipv4_addr_.sin_port) {
return a.ipv4_addr_.sin_port < b.ipv4_addr_.sin_port;
}
return std::memcmp(&a.ipv4_addr_.sin_addr, &b.ipv4_addr_.sin_addr, sizeof(a.ipv4_addr_.sin_addr)) < 0;
} else if (a.get_address_family() == AF_INET6) {
if (a.ipv6_addr_.sin6_port != b.ipv6_addr_.sin6_port) {
return a.ipv6_addr_.sin6_port < b.ipv6_addr_.sin6_port;
}
return std::memcmp(&a.ipv6_addr_.sin6_addr, &b.ipv6_addr_.sin6_addr, sizeof(a.ipv6_addr_.sin6_addr)) < 0;
}
LOG(FATAL) << "Unknown address family";
return false;
}
StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address) {
if (!address.is_valid()) {
return builder << "[invalid]";
}
if (address.get_address_family() == AF_INET) {
return builder << "[" << address.get_ip_str() << ":" << address.get_port() << "]";
} else {
CHECK(address.get_address_family() == AF_INET6);
return builder << "[[" << address.get_ip_str() << "]:" << address.get_port() << "]";
}
}
} // namespace td

View file

@ -0,0 +1,100 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#if !TD_WINDOWS
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif
namespace td {
Result<string> idn_to_ascii(CSlice host);
class SocketFd;
class IPAddress {
public:
IPAddress();
bool is_valid() const;
bool is_ipv4() const;
bool is_ipv6() const;
bool is_reserved() const;
int get_port() const;
void set_port(int port);
uint32 get_ipv4() const;
Slice get_ipv6() const;
Slice get_ip_str() const;
IPAddress get_any_addr() const;
static Result<IPAddress> get_ipv4_address(CSlice host);
static Result<IPAddress> get_ipv6_address(CSlice host);
Status init_ipv6_port(CSlice ipv6, int port) TD_WARN_UNUSED_RESULT;
Status init_ipv6_as_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT;
Status init_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT;
Status init_host_port(CSlice host, int port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT;
Status init_host_port(CSlice host, CSlice port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT;
Status init_host_port(CSlice host_port) TD_WARN_UNUSED_RESULT;
Status init_socket_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT;
Status init_peer_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT;
friend bool operator==(const IPAddress &a, const IPAddress &b);
friend bool operator<(const IPAddress &a, const IPAddress &b);
// for internal usage only
const sockaddr *get_sockaddr() const;
size_t get_sockaddr_len() const;
int get_address_family() const;
static CSlice ipv4_to_str(uint32 ipv4);
static CSlice ipv6_to_str(Slice ipv6);
Status init_sockaddr(sockaddr *addr);
Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;
private:
union {
sockaddr sockaddr_;
sockaddr_in ipv4_addr_;
sockaddr_in6 ipv6_addr_;
};
static constexpr socklen_t storage_size() {
return sizeof(ipv6_addr_);
}
bool is_valid_;
void init_ipv4_any();
void init_ipv6_any();
};
StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address);
} // namespace td

View file

@ -0,0 +1,54 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Slice.h"
#if TD_PORT_POSIX
#include <sys/uio.h>
#endif
namespace td {
#if TD_PORT_POSIX
using IoSlice = struct iovec;
inline IoSlice as_io_slice(Slice slice) {
IoSlice res;
res.iov_len = slice.size();
res.iov_base = const_cast<char *>(slice.data());
return res;
}
inline Slice as_slice(const IoSlice io_slice) {
return Slice(static_cast<const char *>(io_slice.iov_base), io_slice.iov_len);
}
#else
using IoSlice = Slice;
inline IoSlice as_io_slice(Slice slice) {
return slice;
}
#endif
} // namespace td

View file

@ -0,0 +1,123 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/MemoryMapping.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
// TODO:
// windows,
// anonymous maps,
// huge pages?
#if TD_WINDOWS
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
namespace td {
class MemoryMapping::Impl {
public:
Impl(MutableSlice data, int64 offset) : data_(data), offset_(offset) {
}
Slice as_slice() const {
return data_.substr(narrow_cast<size_t>(offset_));
}
MutableSlice as_mutable_slice() const {
return {};
}
private:
MutableSlice data_;
int64 offset_;
};
static Result<int64> get_page_size() {
#if TD_WINDOWS
return Status::Error("Unimplemented");
#else
static Result<int64> page_size = []() -> Result<int64> {
auto page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
return OS_ERROR("Can't load page size from sysconf");
}
return page_size;
}();
return page_size.clone();
#endif
}
Result<MemoryMapping> MemoryMapping::create_anonymous(const MemoryMapping::Options &options) {
return Status::Error("Unsupported yet");
}
Result<MemoryMapping> MemoryMapping::create_from_file(const FileFd &file_fd, const MemoryMapping::Options &options) {
#if TD_WINDOWS
return Status::Error("Unsupported yet");
#else
if (file_fd.empty()) {
return Status::Error("Can't create memory mapping: file is empty");
}
TRY_RESULT(stat, file_fd.stat());
auto fd = file_fd.get_native_fd().fd();
auto begin = options.offset;
if (begin < 0) {
return Status::Error(PSLICE() << "Can't create memory mapping: negative offset " << options.offset);
}
int64 end;
if (options.size < 0) {
end = stat.size_;
} else {
end = begin + stat.size_;
}
TRY_RESULT(page_size, get_page_size());
auto fixed_begin = begin / page_size * page_size;
auto data_offset = begin - 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) {
return OS_ERROR("mmap call failed");
}
return MemoryMapping(make_unique<Impl>(MutableSlice(static_cast<char *>(data), data_size), data_offset));
#endif
}
MemoryMapping::MemoryMapping(MemoryMapping &&other) = default;
MemoryMapping &MemoryMapping::operator=(MemoryMapping &&other) = default;
MemoryMapping::~MemoryMapping() = default;
MemoryMapping::MemoryMapping(unique_ptr<Impl> impl) : impl_(std::move(impl)) {
}
Slice MemoryMapping::as_slice() const {
return impl_->as_slice();
}
MutableSlice MemoryMapping::as_mutable_slice() {
return impl_->as_mutable_slice();
}
} // namespace td

View file

@ -0,0 +1,64 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class MemoryMapping {
public:
struct Options {
int64 offset{0};
int64 size{-1};
Options() {
}
Options &with_offset(int64 new_offset) {
offset = new_offset;
return *this;
}
Options &with_size(int64 new_size) {
size = new_size;
return *this;
}
};
static Result<MemoryMapping> create_anonymous(const Options &options = {});
static Result<MemoryMapping> create_from_file(const FileFd &file, const Options &options = {});
Slice as_slice() const;
MutableSlice as_mutable_slice(); // returns empty slice if memory is read-only
MemoryMapping(const MemoryMapping &other) = delete;
const MemoryMapping &operator=(const MemoryMapping &other) = delete;
MemoryMapping(MemoryMapping &&other);
MemoryMapping &operator=(MemoryMapping &&other);
~MemoryMapping();
private:
class Impl;
unique_ptr<Impl> impl_;
explicit MemoryMapping(unique_ptr<Impl> impl);
};
} // namespace td

View file

@ -0,0 +1,47 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/detail/Epoll.h"
#include "td/utils/port/detail/KQueue.h"
#include "td/utils/port/detail/Poll.h"
#include "td/utils/port/detail/Select.h"
#include "td/utils/port/detail/WineventPoll.h"
namespace td {
// clang-format off
#if TD_POLL_EPOLL
using Poll = detail::Epoll;
#elif TD_POLL_KQUEUE
using Poll = detail::KQueue;
#elif TD_POLL_WINEVENT
using Poll = detail::WineventPoll;
#elif TD_POLL_POLL
using Poll = detail::Poll;
#elif TD_POLL_SELECT
using Poll = detail::Select;
#endif
// clang-format on
} // namespace td

View file

@ -0,0 +1,40 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollFlags.h"
namespace td {
class PollBase {
public:
PollBase() = default;
PollBase(const PollBase &) = delete;
PollBase &operator=(const PollBase &) = delete;
PollBase(PollBase &&) = default;
PollBase &operator=(PollBase &&) = default;
virtual ~PollBase() = default;
virtual void init() = 0;
virtual void clear() = 0;
virtual void subscribe(PollableFd fd, PollFlags flags) = 0;
virtual void unsubscribe(PollableFdRef fd) = 0;
virtual void unsubscribe_before_close(PollableFdRef fd) = 0;
virtual void run(int timeout_ms) = 0;
};
} // namespace td

View file

@ -0,0 +1,83 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/PollFlags.h"
namespace td {
bool PollFlagsSet::write_flags(PollFlags flags) {
if (flags.empty()) {
return false;
}
auto old_flags = to_write_.fetch_or(flags.raw(), std::memory_order_relaxed);
return (flags.raw() & ~old_flags) != 0;
}
bool PollFlagsSet::write_flags_local(PollFlags flags) {
return flags_.add_flags(flags);
}
bool PollFlagsSet::flush() const {
if (to_write_.load(std::memory_order_relaxed) == 0) {
return false;
}
auto to_write = to_write_.exchange(0, std::memory_order_relaxed);
auto old_flags = flags_;
flags_.add_flags(PollFlags::from_raw(to_write));
if (flags_.can_close()) {
flags_.remove_flags(PollFlags::Write());
}
return flags_ != old_flags;
}
PollFlags PollFlagsSet::read_flags() const {
flush();
return flags_;
}
PollFlags PollFlagsSet::read_flags_local() const {
return flags_;
}
void PollFlagsSet::clear_flags(PollFlags flags) {
flags_.remove_flags(flags);
}
void PollFlagsSet::clear() {
to_write_ = 0;
flags_ = {};
}
StringBuilder &operator<<(StringBuilder &sb, PollFlags flags) {
sb << '[';
if (flags.can_read()) {
sb << 'R';
}
if (flags.can_write()) {
sb << 'W';
}
if (flags.can_close()) {
sb << 'C';
}
if (flags.has_pending_error()) {
sb << 'E';
}
return sb << ']';
}
} // namespace td

View file

@ -0,0 +1,134 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/StringBuilder.h"
#include <atomic>
namespace td {
class PollFlags {
public:
using Raw = int32;
bool can_read() const {
return has_flags(Read());
}
bool can_write() const {
return has_flags(Write());
}
bool can_close() const {
return has_flags(Close());
}
bool has_pending_error() const {
return has_flags(Error());
}
void remove_flags(PollFlags flags) {
remove_flags(flags.raw());
}
bool add_flags(PollFlags flags) {
auto old_flags = flags_;
add_flags(flags.raw());
return old_flags != flags_;
}
bool has_flags(PollFlags flags) const {
return has_flags(flags.raw());
}
bool empty() const {
return flags_ == 0;
}
Raw raw() const {
return flags_;
}
static PollFlags from_raw(Raw raw) {
return PollFlags(raw);
}
PollFlags() = default;
bool operator==(const PollFlags &other) const {
return flags_ == other.flags_;
}
bool operator!=(const PollFlags &other) const {
return !(*this == other);
}
PollFlags operator|(const PollFlags other) const {
return from_raw(raw() | other.raw());
}
static PollFlags Write() {
return PollFlags(Flag::Write);
}
static PollFlags Error() {
return PollFlags(Flag::Error);
}
static PollFlags Close() {
return PollFlags(Flag::Close);
}
static PollFlags Read() {
return PollFlags(Flag::Read);
}
static PollFlags ReadWrite() {
return Read() | Write();
}
private:
enum class Flag : Raw { Write = 0x001, Read = 0x002, Close = 0x004, Error = 0x008, None = 0 };
Raw flags_{static_cast<Raw>(Flag::None)};
explicit PollFlags(Raw raw) : flags_(raw) {
}
explicit PollFlags(Flag flag) : PollFlags(static_cast<Raw>(flag)) {
}
PollFlags &add_flags(Raw flags) {
flags_ |= flags;
return *this;
}
PollFlags &remove_flags(Raw flags) {
flags_ &= ~flags;
return *this;
}
bool has_flags(Raw flags) const {
return (flags_ & flags) == flags;
}
};
StringBuilder &operator<<(StringBuilder &sb, PollFlags flags);
class PollFlagsSet {
public:
// write flags from any thread
// this is the only function that should be called from other threads
bool write_flags(PollFlags flags);
bool write_flags_local(PollFlags flags);
bool flush() const;
PollFlags read_flags() const;
PollFlags read_flags_local() const;
void clear_flags(PollFlags flags);
void clear();
private:
mutable std::atomic<PollFlags::Raw> to_write_{0};
mutable PollFlags flags_;
};
} // namespace td

View file

@ -0,0 +1,161 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
#if TD_PORT_POSIX
#include <pthread.h>
#endif
#include <memory>
namespace td {
class RwMutex {
public:
RwMutex() {
init();
}
RwMutex(const RwMutex &) = delete;
RwMutex &operator=(const RwMutex &) = delete;
RwMutex(RwMutex &&other) {
init();
other.clear();
}
RwMutex &operator=(RwMutex &&other) {
other.clear();
return *this;
}
~RwMutex() {
clear();
}
bool empty() const {
return !is_valid_;
}
void init();
void clear();
struct ReadUnlock {
void operator()(RwMutex *ptr) {
ptr->unlock_read_unsafe();
}
};
struct WriteUnlock {
void operator()(RwMutex *ptr) {
ptr->unlock_write_unsafe();
}
};
using ReadLock = std::unique_ptr<RwMutex, ReadUnlock>;
using WriteLock = std::unique_ptr<RwMutex, WriteUnlock>;
Result<ReadLock> lock_read() TD_WARN_UNUSED_RESULT {
lock_read_unsafe();
return ReadLock(this);
}
Result<WriteLock> lock_write() TD_WARN_UNUSED_RESULT {
lock_write_unsafe();
return WriteLock(this);
}
void lock_read_unsafe();
void lock_write_unsafe();
void unlock_read_unsafe();
void unlock_write_unsafe();
private:
bool is_valid_ = false;
#if TD_PORT_POSIX
pthread_rwlock_t mutex_;
#elif TD_PORT_WINDOWS
unique_ptr<SRWLOCK> mutex_;
#endif
};
inline void RwMutex::init() {
CHECK(empty());
is_valid_ = true;
#if TD_PORT_POSIX
pthread_rwlock_init(&mutex_, nullptr);
#elif TD_PORT_WINDOWS
mutex_ = make_unique<SRWLOCK>();
InitializeSRWLock(mutex_.get());
#endif
}
inline void RwMutex::clear() {
if (is_valid_) {
#if TD_PORT_POSIX
pthread_rwlock_destroy(&mutex_);
#elif TD_PORT_WINDOWS
mutex_.release();
#endif
is_valid_ = false;
}
}
inline void RwMutex::lock_read_unsafe() {
CHECK(!empty());
// TODO error handling
#if TD_PORT_POSIX
pthread_rwlock_rdlock(&mutex_);
#elif TD_PORT_WINDOWS
AcquireSRWLockShared(mutex_.get());
#endif
}
inline void RwMutex::lock_write_unsafe() {
CHECK(!empty());
#if TD_PORT_POSIX
pthread_rwlock_wrlock(&mutex_);
#elif TD_PORT_WINDOWS
AcquireSRWLockExclusive(mutex_.get());
#endif
}
inline void RwMutex::unlock_read_unsafe() {
CHECK(!empty());
#if TD_PORT_POSIX
pthread_rwlock_unlock(&mutex_);
#elif TD_PORT_WINDOWS
ReleaseSRWLockShared(mutex_.get());
#endif
}
inline void RwMutex::unlock_write_unsafe() {
CHECK(!empty());
#if TD_PORT_POSIX
pthread_rwlock_unlock(&mutex_);
#elif TD_PORT_WINDOWS
ReleaseSRWLockExclusive(mutex_.get());
#endif
}
} // namespace td

View file

@ -0,0 +1,373 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/ServerSocketFd.h"
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/PollFlags.h"
#if TD_PORT_POSIX
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#if TD_PORT_WINDOWS
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/SpinLock.h"
#include "td/utils/VectorQueue.h"
#endif
#include <atomic>
#include <cstring>
namespace td {
namespace detail {
#if TD_PORT_WINDOWS
class ServerSocketFdImpl : private Iocp::Callback {
public:
ServerSocketFdImpl(NativeFd fd, int socket_family) : info_(std::move(fd)), socket_family_(socket_family) {
VLOG(fd) << get_native_fd() << " create ServerSocketFd";
Iocp::get()->subscribe(get_native_fd(), this);
notify_iocp_read();
}
void close() {
notify_iocp_close();
}
PollableFdInfo &get_poll_info() {
return info_;
}
const PollableFdInfo &get_poll_info() const {
return info_;
}
const NativeFd &get_native_fd() const {
return info_.native_fd();
}
Result<SocketFd> accept() {
auto lock = lock_.lock();
if (accepted_.empty()) {
get_poll_info().clear_flags(PollFlags::Read());
return Status::Error(-1, "Operation would block");
}
return accepted_.pop();
}
Status get_pending_error() {
Status res;
{
auto lock = lock_.lock();
if (!pending_errors_.empty()) {
res = pending_errors_.pop();
}
if (res.is_ok()) {
get_poll_info().clear_flags(PollFlags::Error());
}
}
return res;
}
private:
PollableFdInfo info_;
SpinLock lock_;
VectorQueue<SocketFd> accepted_;
VectorQueue<Status> pending_errors_;
static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16;
char addr_buf_[MAX_ADDR_SIZE * 2];
bool close_flag_{false};
std::atomic<int> refcnt_{1};
bool is_read_active_{false};
WSAOVERLAPPED read_overlapped_;
char close_overlapped_;
NativeFd accept_socket_;
int socket_family_;
void on_close() {
close_flag_ = true;
info_.set_native_fd({});
}
void on_read() {
VLOG(fd) << get_native_fd() << " on_read";
if (is_read_active_) {
is_read_active_ = false;
auto r_socket = [&]() -> Result<SocketFd> {
auto from = get_native_fd().socket();
auto status = setsockopt(accept_socket_.socket(), SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
reinterpret_cast<const char *>(&from), sizeof(from));
if (status != 0) {
return OS_SOCKET_ERROR("Failed to set SO_UPDATE_ACCEPT_CONTEXT options");
}
return SocketFd::from_native_fd(std::move(accept_socket_));
}();
VLOG(fd) << get_native_fd() << " finish accept";
if (r_socket.is_error()) {
return on_error(r_socket.move_as_error());
}
{
auto lock = lock_.lock();
accepted_.push(r_socket.move_as_ok());
}
get_poll_info().add_flags_from_poll(PollFlags::Read());
}
loop_read();
}
void loop_read() {
CHECK(!is_read_active_);
accept_socket_ = NativeFd(socket(socket_family_, SOCK_STREAM, 0));
std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
VLOG(fd) << get_native_fd() << " start accept";
BOOL status = AcceptEx(get_native_fd().socket(), accept_socket_.socket(), addr_buf_, 0, MAX_ADDR_SIZE,
MAX_ADDR_SIZE, nullptr, &read_overlapped_);
if (status == TRUE || check_status("Failed to accept connection")) {
inc_refcnt();
is_read_active_ = true;
}
}
bool check_status(Slice message) {
auto last_error = WSAGetLastError();
if (last_error == ERROR_IO_PENDING) {
return true;
}
on_error(OS_SOCKET_ERROR(message));
return false;
}
bool dec_refcnt() {
if (--refcnt_ == 0) {
delete this;
return true;
}
return false;
}
void inc_refcnt() {
CHECK(refcnt_ != 0);
refcnt_++;
}
void on_error(Status status) {
{
auto lock = lock_.lock();
pending_errors_.push(std::move(status));
}
get_poll_info().add_flags_from_poll(PollFlags::Error());
}
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
// called from other thread
if (dec_refcnt() || close_flag_) {
VLOG(fd) << "Ignore IOCP (server socket is closing)";
return;
}
if (r_size.is_error()) {
return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
}
if (overlapped == nullptr) {
return on_read();
}
if (overlapped == &read_overlapped_) {
return on_read();
}
if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
return on_close();
}
UNREACHABLE();
}
void notify_iocp_read() {
VLOG(fd) << get_native_fd() << " notify_read";
inc_refcnt();
Iocp::get()->post(0, this, nullptr);
}
void notify_iocp_close() {
VLOG(fd) << get_native_fd() << " notify_close";
Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
}
};
void ServerSocketFdImplDeleter::operator()(ServerSocketFdImpl *impl) {
impl->close();
}
#elif TD_PORT_POSIX
class ServerSocketFdImpl {
public:
explicit ServerSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
}
PollableFdInfo &get_poll_info() {
return info_;
}
const PollableFdInfo &get_poll_info() const {
return info_;
}
const NativeFd &get_native_fd() const {
return info_.native_fd();
}
Result<SocketFd> accept() {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
int native_fd = get_native_fd().socket();
int r_fd = detail::skip_eintr([&] { return ::accept(native_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len); });
auto accept_errno = errno;
if (r_fd >= 0) {
return SocketFd::from_native_fd(NativeFd(r_fd));
}
if (accept_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| accept_errno == EWOULDBLOCK
#endif
) {
get_poll_info().clear_flags(PollFlags::Read());
return Status::Error(-1, "Operation would block");
}
auto error = Status::PosixError(accept_errno, PSLICE() << "Accept from " << get_native_fd() << " has failed");
switch (accept_errno) {
case EBADF:
case EFAULT:
case EINVAL:
case ENOTSOCK:
case EOPNOTSUPP:
LOG(FATAL) << error;
UNREACHABLE();
break;
default:
LOG(ERROR) << error;
// fallthrough
case EMFILE:
case ENFILE:
case ECONNABORTED: //???
get_poll_info().clear_flags(PollFlags::Read());
get_poll_info().add_flags(PollFlags::Close());
return std::move(error);
}
}
Status get_pending_error() {
if (!get_poll_info().get_flags().has_pending_error()) {
return Status::OK();
}
TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
get_poll_info().clear_flags(PollFlags::Error());
return Status::OK();
}
private:
PollableFdInfo info_;
};
void ServerSocketFdImplDeleter::operator()(ServerSocketFdImpl *impl) {
delete impl;
}
#endif
} // namespace detail
ServerSocketFd::ServerSocketFd() = default;
ServerSocketFd::ServerSocketFd(ServerSocketFd &&) = default;
ServerSocketFd &ServerSocketFd::operator=(ServerSocketFd &&) = default;
ServerSocketFd::~ServerSocketFd() = default;
ServerSocketFd::ServerSocketFd(unique_ptr<detail::ServerSocketFdImpl> impl) : impl_(impl.release()) {
}
PollableFdInfo &ServerSocketFd::get_poll_info() {
return impl_->get_poll_info();
}
const PollableFdInfo &ServerSocketFd::get_poll_info() const {
return impl_->get_poll_info();
}
Status ServerSocketFd::get_pending_error() {
return impl_->get_pending_error();
}
const NativeFd &ServerSocketFd::get_native_fd() const {
return impl_->get_native_fd();
}
Result<SocketFd> ServerSocketFd::accept() {
return impl_->accept();
}
void ServerSocketFd::close() {
impl_.reset();
}
bool ServerSocketFd::empty() const {
return !impl_;
}
Result<ServerSocketFd> ServerSocketFd::open(int32 port, CSlice addr) {
IPAddress address;
TRY_STATUS(address.init_ipv4_port(addr, port));
NativeFd fd{socket(address.get_address_family(), SOCK_STREAM, 0)};
if (!fd) {
return OS_SOCKET_ERROR("Failed to create a socket");
}
TRY_STATUS(fd.set_is_blocking_unsafe(false));
auto sock = fd.socket();
linger ling = {0, 0};
#if TD_PORT_POSIX
int flags = 1;
#ifdef SO_REUSEPORT
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const char *>(&flags), sizeof(flags));
#endif
#elif TD_PORT_WINDOWS
BOOL flags = TRUE;
#endif
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
setsockopt(sock, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char *>(&ling), sizeof(ling));
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
int e_bind = bind(sock, address.get_sockaddr(), static_cast<socklen_t>(address.get_sockaddr_len()));
if (e_bind != 0) {
return OS_SOCKET_ERROR("Failed to bind a socket");
}
// TODO: magic constant
int e_listen = listen(sock, 8192);
if (e_listen != 0) {
return OS_SOCKET_ERROR("Failed to listen on a socket");
}
#if TD_PORT_POSIX
auto impl = make_unique<detail::ServerSocketFdImpl>(std::move(fd));
#elif TD_PORT_WINDOWS
auto impl = make_unique<detail::ServerSocketFdImpl>(std::move(fd), address.get_address_family());
#endif
return ServerSocketFd(std::move(impl));
}
} // namespace td

View file

@ -0,0 +1,66 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
namespace detail {
class ServerSocketFdImpl;
class ServerSocketFdImplDeleter {
public:
void operator()(ServerSocketFdImpl *impl);
};
} // namespace detail
class ServerSocketFd {
public:
ServerSocketFd();
ServerSocketFd(const ServerSocketFd &) = delete;
ServerSocketFd &operator=(const ServerSocketFd &) = delete;
ServerSocketFd(ServerSocketFd &&);
ServerSocketFd &operator=(ServerSocketFd &&);
~ServerSocketFd();
static Result<ServerSocketFd> open(int32 port, CSlice addr = CSlice("0.0.0.0")) TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
Status get_pending_error() TD_WARN_UNUSED_RESULT;
Result<SocketFd> accept() TD_WARN_UNUSED_RESULT;
void close();
bool empty() const;
const NativeFd &get_native_fd() const;
private:
std::unique_ptr<detail::ServerSocketFdImpl, detail::ServerSocketFdImplDeleter> impl_;
explicit ServerSocketFd(unique_ptr<detail::ServerSocketFdImpl> impl);
};
} // namespace td

View file

@ -0,0 +1,632 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/SocketFd.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/PollFlags.h"
#if TD_PORT_WINDOWS
#include "td/utils/buffer.h"
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/SpinLock.h"
#include "td/utils/VectorQueue.h"
#endif
#if TD_PORT_POSIX
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <atomic>
#include <cstring>
namespace td {
namespace detail {
#if TD_PORT_WINDOWS
class SocketFdImpl : private Iocp::Callback {
public:
explicit SocketFdImpl(NativeFd native_fd) : info(std::move(native_fd)) {
VLOG(fd) << get_native_fd() << " create from native_fd";
get_poll_info().add_flags(PollFlags::Write());
Iocp::get()->subscribe(get_native_fd(), this);
is_read_active_ = true;
notify_iocp_connected();
}
SocketFdImpl(NativeFd native_fd, const IPAddress &addr) : info(std::move(native_fd)) {
VLOG(fd) << get_native_fd() << " create from native_fd and connect";
get_poll_info().add_flags(PollFlags::Write());
Iocp::get()->subscribe(get_native_fd(), this);
LPFN_CONNECTEX ConnectExPtr = nullptr;
GUID guid = WSAID_CONNECTEX;
DWORD numBytes;
auto error =
::WSAIoctl(get_native_fd().socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, static_cast<void *>(&guid),
sizeof(guid), static_cast<void *>(&ConnectExPtr), sizeof(ConnectExPtr), &numBytes, nullptr, nullptr);
if (error) {
on_error(OS_SOCKET_ERROR("WSAIoctl failed"));
return;
}
std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
inc_refcnt();
is_read_active_ = true;
auto status = ConnectExPtr(get_native_fd().socket(), addr.get_sockaddr(), narrow_cast<int>(addr.get_sockaddr_len()),
nullptr, 0, nullptr, &read_overlapped_);
if (status == TRUE || !check_status("Failed to connect")) {
is_read_active_ = false;
dec_refcnt();
}
}
void close() {
notify_iocp_close();
}
PollableFdInfo &get_poll_info() {
return info;
}
const PollableFdInfo &get_poll_info() const {
return info;
}
const NativeFd &get_native_fd() const {
return info.native_fd();
}
Result<size_t> write(Slice data) {
// LOG(ERROR) << "Write: " << format::as_hex_dump<0>(data);
output_writer_.append(data);
return write_finish(data.size());
}
Result<size_t> writev(Span<IoSlice> slices) {
size_t total_size = 0;
for (auto io_slice : slices) {
total_size += as_slice(io_slice).size();
}
auto left_size = total_size;
for (auto io_slice : slices) {
auto slice = as_slice(io_slice);
output_writer_.append(slice, left_size);
left_size -= slice.size();
}
return write_finish(total_size);
}
Result<size_t> write_finish(size_t total_size) {
if (is_write_waiting_) {
auto lock = lock_.lock();
is_write_waiting_ = false;
lock.reset();
notify_iocp_write();
}
return total_size;
}
Result<size_t> read(MutableSlice slice) {
if (get_poll_info().get_flags().has_pending_error()) {
TRY_STATUS(get_pending_error());
}
input_reader_.sync_with_writer();
auto res = input_reader_.advance(td::min(slice.size(), input_reader_.size()), slice);
if (res == 0) {
get_poll_info().clear_flags(PollFlags::Read());
} else {
// LOG(ERROR) << "Read: " << format::as_hex_dump<0>(Slice(slice.substr(0, res)));
}
return res;
}
Status get_pending_error() {
Status res;
{
auto lock = lock_.lock();
if (!pending_errors_.empty()) {
res = pending_errors_.pop();
}
if (res.is_ok()) {
get_poll_info().clear_flags(PollFlags::Error());
}
}
return res;
}
private:
PollableFdInfo info;
SpinLock lock_;
std::atomic<int> refcnt_{1};
bool close_flag_{false};
bool is_connected_{false};
bool is_read_active_{false};
ChainBufferWriter input_writer_;
ChainBufferReader input_reader_ = input_writer_.extract_reader();
WSAOVERLAPPED read_overlapped_;
VectorQueue<Status> pending_errors_;
bool is_write_active_{false};
std::atomic<bool> is_write_waiting_{false};
ChainBufferWriter output_writer_;
ChainBufferReader output_reader_ = output_writer_.extract_reader();
WSAOVERLAPPED write_overlapped_;
char close_overlapped_;
bool check_status(Slice message) {
auto last_error = WSAGetLastError();
if (last_error == ERROR_IO_PENDING) {
return true;
}
on_error(OS_SOCKET_ERROR(message));
return false;
}
void loop_read() {
CHECK(is_connected_);
CHECK(!is_read_active_);
if (close_flag_) {
return;
}
std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
auto dest = input_writer_.prepare_append();
WSABUF buf;
buf.len = narrow_cast<ULONG>(dest.size());
buf.buf = dest.data();
DWORD flags = 0;
int status = WSARecv(get_native_fd().socket(), &buf, 1, nullptr, &flags, &read_overlapped_, nullptr);
if (status == 0 || check_status("Failed to read from connection")) {
inc_refcnt();
is_read_active_ = true;
}
}
void loop_write() {
CHECK(is_connected_);
CHECK(!is_write_active_);
output_reader_.sync_with_writer();
auto to_write = output_reader_.prepare_read();
if (to_write.empty()) {
auto lock = lock_.lock();
output_reader_.sync_with_writer();
to_write = output_reader_.prepare_read();
if (to_write.empty()) {
is_write_waiting_ = true;
return;
}
}
if (to_write.empty()) {
return;
}
std::memset(&write_overlapped_, 0, sizeof(write_overlapped_));
constexpr size_t buf_size = 20;
WSABUF buf[buf_size];
auto it = output_reader_.clone();
size_t buf_i;
for (buf_i = 0; buf_i < buf_size; buf_i++) {
auto src = it.prepare_read();
if (src.empty()) {
break;
}
buf[buf_i].len = narrow_cast<ULONG>(src.size());
buf[buf_i].buf = const_cast<CHAR *>(src.data());
it.confirm_read(src.size());
}
int status =
WSASend(get_native_fd().socket(), buf, narrow_cast<DWORD>(buf_i), nullptr, 0, &write_overlapped_, nullptr);
if (status == 0 || check_status("Failed to write to connection")) {
inc_refcnt();
is_write_active_ = true;
}
}
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
// called from other thread
if (dec_refcnt() || close_flag_) {
VLOG(fd) << "Ignore IOCP (socket is closing)";
return;
}
if (r_size.is_error()) {
return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
}
if (!is_connected_ && overlapped == &read_overlapped_) {
return on_connected();
}
auto size = r_size.move_as_ok();
if (overlapped == &write_overlapped_) {
return on_write(size);
}
if (overlapped == nullptr) {
CHECK(size == 0);
return on_write(size);
}
if (overlapped == &read_overlapped_) {
return on_read(size);
}
if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
return on_close();
}
UNREACHABLE();
}
void on_error(Status status) {
VLOG(fd) << get_native_fd() << " on error " << status;
{
auto lock = lock_.lock();
pending_errors_.push(std::move(status));
}
get_poll_info().add_flags_from_poll(PollFlags::Error());
}
void on_connected() {
VLOG(fd) << get_native_fd() << " on connected";
CHECK(!is_connected_);
CHECK(is_read_active_);
is_connected_ = true;
is_read_active_ = false;
loop_read();
loop_write();
}
void on_read(size_t size) {
VLOG(fd) << get_native_fd() << " on read " << size;
CHECK(is_read_active_);
is_read_active_ = false;
if (size == 0) {
get_poll_info().add_flags_from_poll(PollFlags::Close());
return;
}
input_writer_.confirm_append(size);
get_poll_info().add_flags_from_poll(PollFlags::Read());
loop_read();
}
void on_write(size_t size) {
VLOG(fd) << get_native_fd() << " on write " << size;
if (size == 0) {
if (is_write_active_) {
return;
}
is_write_active_ = true;
}
CHECK(is_write_active_);
is_write_active_ = false;
output_reader_.advance(size);
loop_write();
}
void on_close() {
VLOG(fd) << get_native_fd() << " on close";
close_flag_ = true;
info.set_native_fd({});
}
bool dec_refcnt() {
VLOG(fd) << get_native_fd() << " dec_refcnt from " << refcnt_;
if (--refcnt_ == 0) {
delete this;
return true;
}
return false;
}
void inc_refcnt() {
CHECK(refcnt_ != 0);
refcnt_++;
VLOG(fd) << get_native_fd() << " inc_refcnt to " << refcnt_;
}
void notify_iocp_write() {
inc_refcnt();
Iocp::get()->post(0, this, nullptr);
}
void notify_iocp_close() {
Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
}
void notify_iocp_connected() {
inc_refcnt();
Iocp::get()->post(0, this, &read_overlapped_);
}
};
void SocketFdImplDeleter::operator()(SocketFdImpl *impl) {
impl->close();
}
class InitWSA {
public:
InitWSA() {
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
auto error = OS_SOCKET_ERROR("Failed to init WSA");
LOG(FATAL) << error;
}
}
};
static InitWSA init_wsa;
#else
class SocketFdImpl {
public:
PollableFdInfo info;
explicit SocketFdImpl(NativeFd fd) : info(std::move(fd)) {
}
PollableFdInfo &get_poll_info() {
return info;
}
const PollableFdInfo &get_poll_info() const {
return info;
}
const NativeFd &get_native_fd() const {
return info.native_fd();
}
Result<size_t> writev(Span<IoSlice> slices) {
int native_fd = get_native_fd().socket();
auto write_res =
detail::skip_eintr([&] { return ::writev(native_fd, slices.begin(), narrow_cast<int>(slices.size())); });
return write_finish(write_res);
}
Result<size_t> write(Slice slice) {
int native_fd = get_native_fd().socket();
auto write_res = detail::skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
return write_finish(write_res);
}
Result<size_t> write_finish(ssize_t write_res) {
auto write_errno = errno;
if (write_res >= 0) {
return narrow_cast<size_t>(write_res);
}
if (write_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| write_errno == EWOULDBLOCK
#endif
) {
get_poll_info().clear_flags(PollFlags::Write());
return 0;
}
auto error = Status::PosixError(write_errno, PSLICE() << "Write to " << get_native_fd() << " has failed");
switch (write_errno) {
case EBADF:
case ENXIO:
case EFAULT:
case EINVAL:
LOG(FATAL) << error;
UNREACHABLE();
default:
LOG(WARNING) << error;
// fallthrough
case ECONNRESET:
case EDQUOT:
case EFBIG:
case EIO:
case ENETDOWN:
case ENETUNREACH:
case ENOSPC:
case EPIPE:
get_poll_info().clear_flags(PollFlags::Write());
get_poll_info().add_flags(PollFlags::Close());
return std::move(error);
}
}
Result<size_t> read(MutableSlice slice) {
if (get_poll_info().get_flags().has_pending_error()) {
TRY_STATUS(get_pending_error());
}
int native_fd = get_native_fd().socket();
CHECK(slice.size() > 0);
auto read_res = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
auto read_errno = errno;
if (read_res >= 0) {
if (read_res == 0) {
errno = 0;
get_poll_info().clear_flags(PollFlags::Read());
get_poll_info().add_flags(PollFlags::Close());
}
return narrow_cast<size_t>(read_res);
}
if (read_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| read_errno == EWOULDBLOCK
#endif
) {
get_poll_info().clear_flags(PollFlags::Read());
return 0;
}
auto error = Status::PosixError(read_errno, PSLICE() << "Read from " << get_native_fd() << " has failed");
switch (read_errno) {
case EISDIR:
case EBADF:
case ENXIO:
case EFAULT:
case EINVAL:
LOG(FATAL) << error;
UNREACHABLE();
default:
LOG(WARNING) << error;
// fallthrough
case ENOTCONN:
case EIO:
case ENOBUFS:
case ENOMEM:
case ECONNRESET:
case ETIMEDOUT:
get_poll_info().clear_flags(PollFlags::Read());
get_poll_info().add_flags(PollFlags::Close());
return std::move(error);
}
}
Status get_pending_error() {
if (!get_poll_info().get_flags().has_pending_error()) {
return Status::OK();
}
TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
get_poll_info().clear_flags(PollFlags::Error());
return Status::OK();
}
};
void SocketFdImplDeleter::operator()(SocketFdImpl *impl) {
delete impl;
}
#endif
#if TD_PORT_POSIX
Status get_socket_pending_error(const NativeFd &fd) {
int error = 0;
socklen_t errlen = sizeof(error);
if (getsockopt(fd.socket(), SOL_SOCKET, SO_ERROR, static_cast<void *>(&error), &errlen) == 0) {
if (error == 0) {
return Status::OK();
}
return Status::PosixError(error, PSLICE() << "Error on " << fd);
}
auto status = OS_SOCKET_ERROR(PSLICE() << "Can't load error on socket " << fd);
LOG(INFO) << "Can't load pending socket error: " << status;
return status;
}
#elif TD_PORT_WINDOWS
Status get_socket_pending_error(const NativeFd &fd, WSAOVERLAPPED *overlapped, Status iocp_error) {
// We need to call WSAGetOverlappedResult() just so WSAGetLastError() will return the correct error. See
// https://stackoverflow.com/questions/28925003/calling-wsagetlasterror-from-an-iocp-thread-return-incorrect-result
DWORD num_bytes = 0;
DWORD flags = 0;
BOOL success = WSAGetOverlappedResult(fd.socket(), overlapped, &num_bytes, false, &flags);
if (success) {
LOG(ERROR) << "WSAGetOverlappedResult succeded after " << iocp_error;
return iocp_error;
}
return OS_SOCKET_ERROR(PSLICE() << "Error on " << fd);
}
#endif
Status init_socket_options(NativeFd &native_fd) {
TRY_STATUS(native_fd.set_is_blocking_unsafe(false));
auto sock = native_fd.socket();
#if TD_PORT_POSIX
int flags = 1;
#elif TD_PORT_WINDOWS
BOOL flags = TRUE;
#endif
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
// TODO: SO_REUSEADDR, SO_KEEPALIVE, TCP_NODELAY, SO_SNDBUF, SO_RCVBUF, TCP_QUICKACK, SO_LINGER
return Status::OK();
}
} // namespace detail
SocketFd::SocketFd() = default;
SocketFd::SocketFd(SocketFd &&) = default;
SocketFd &SocketFd::operator=(SocketFd &&) = default;
SocketFd::~SocketFd() = default;
SocketFd::SocketFd(unique_ptr<detail::SocketFdImpl> impl) : impl_(impl.release()) {
}
Result<SocketFd> SocketFd::from_native_fd(NativeFd fd) {
TRY_STATUS(detail::init_socket_options(fd));
return SocketFd(make_unique<detail::SocketFdImpl>(std::move(fd)));
}
Result<SocketFd> SocketFd::open(const IPAddress &address) {
NativeFd native_fd{socket(address.get_address_family(), SOCK_STREAM, IPPROTO_TCP)};
if (!native_fd) {
return OS_SOCKET_ERROR("Failed to create a socket");
}
TRY_STATUS(detail::init_socket_options(native_fd));
#if TD_PORT_POSIX
int e_connect =
connect(native_fd.socket(), address.get_sockaddr(), narrow_cast<socklen_t>(address.get_sockaddr_len()));
if (e_connect == -1) {
auto connect_errno = errno;
if (connect_errno != EINPROGRESS) {
return Status::PosixError(connect_errno, PSLICE() << "Failed to connect to " << address);
}
}
return SocketFd(make_unique<detail::SocketFdImpl>(std::move(native_fd)));
#elif TD_PORT_WINDOWS
auto bind_addr = address.get_any_addr();
auto e_bind = bind(native_fd.socket(), bind_addr.get_sockaddr(), narrow_cast<int>(bind_addr.get_sockaddr_len()));
if (e_bind != 0) {
return OS_SOCKET_ERROR("Failed to bind a socket");
}
return SocketFd(make_unique<detail::SocketFdImpl>(std::move(native_fd), address));
#endif
}
void SocketFd::close() {
impl_.reset();
}
bool SocketFd::empty() const {
return !impl_;
}
PollableFdInfo &SocketFd::get_poll_info() {
return impl_->get_poll_info();
}
const PollableFdInfo &SocketFd::get_poll_info() const {
return impl_->get_poll_info();
}
const NativeFd &SocketFd::get_native_fd() const {
return impl_->get_native_fd();
}
Status SocketFd::get_pending_error() {
return impl_->get_pending_error();
}
Result<size_t> SocketFd::write(Slice slice) {
return impl_->write(slice);
}
Result<size_t> SocketFd::writev(Span<IoSlice> slices) {
return impl_->writev(slices);
}
Result<size_t> SocketFd::read(MutableSlice slice) {
return impl_->read(slice);
}
} // namespace td

View file

@ -0,0 +1,82 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/IoSlice.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
namespace detail {
class SocketFdImpl;
class SocketFdImplDeleter {
public:
void operator()(SocketFdImpl *impl);
};
} // namespace detail
class SocketFd {
public:
SocketFd();
SocketFd(const SocketFd &) = delete;
SocketFd &operator=(const SocketFd &) = delete;
SocketFd(SocketFd &&);
SocketFd &operator=(SocketFd &&);
~SocketFd();
static Result<SocketFd> open(const IPAddress &address) TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
Status get_pending_error() TD_WARN_UNUSED_RESULT;
Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT;
Result<size_t> writev(Span<IoSlice> slices) TD_WARN_UNUSED_RESULT;
Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
const NativeFd &get_native_fd() const;
static Result<SocketFd> from_native_fd(NativeFd fd);
void close();
bool empty() const;
private:
std::unique_ptr<detail::SocketFdImpl, detail::SocketFdImplDeleter> impl_;
explicit SocketFd(unique_ptr<detail::SocketFdImpl> impl);
};
namespace detail {
#if TD_PORT_POSIX
Status get_socket_pending_error(const NativeFd &fd);
#elif TD_PORT_WINDOWS
Status get_socket_pending_error(const NativeFd &fd, WSAOVERLAPPED *overlapped, Status iocp_error);
#endif
} // namespace detail
} // namespace td

View file

@ -0,0 +1,373 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/Stat.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/FileFd.h"
#if TD_PORT_POSIX
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/ScopeGuard.h"
#include <utility>
#if TD_DARWIN
#include <mach/mach.h>
#include <sys/time.h>
#endif
// We don't want warnings from system headers
#if TD_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
#include <sys/stat.h>
#if TD_GCC
#pragma GCC diagnostic pop
#endif
#if TD_ANDROID || TD_TIZEN
#include <sys/syscall.h>
#endif
namespace td {
namespace detail {
template <class...>
struct voider {
using type = void;
};
template <class... T>
using void_t = typename voider<T...>::type;
template <class T, class = void>
struct TimeNsec {
static std::pair<int, int> get(const T &) {
T().warning("Platform lacks support of precise access/modification file times, comment this line to continue");
return {0, 0};
}
};
// remove libc compatibility hacks if any: we have our own hacks
#ifdef st_atimespec
#undef st_atimespec
#endif
#ifdef st_atimensec
#undef st_atimensec
#endif
#ifdef st_atime_nsec
#undef st_atime_nsec
#endif
template <class T>
struct TimeNsec<T, void_t<char, decltype(T::st_atimespec), decltype(T::st_mtimespec)>> {
static std::pair<decltype(decltype(T::st_atimespec)::tv_nsec), decltype(decltype(T::st_mtimespec)::tv_nsec)> get(
const T &s) {
return {s.st_atimespec.tv_nsec, s.st_mtimespec.tv_nsec};
}
};
template <class T>
struct TimeNsec<T, void_t<short, decltype(T::st_atimensec), decltype(T::st_mtimensec)>> {
static std::pair<decltype(T::st_atimensec), decltype(T::st_mtimensec)> get(const T &s) {
return {s.st_atimensec, s.st_mtimensec};
}
};
template <class T>
struct TimeNsec<T, void_t<int, decltype(T::st_atim), decltype(T::st_mtim)>> {
static std::pair<decltype(decltype(T::st_atim)::tv_nsec), decltype(decltype(T::st_mtim)::tv_nsec)> get(const T &s) {
return {s.st_atim.tv_nsec, s.st_mtim.tv_nsec};
}
};
template <class T>
struct TimeNsec<T, void_t<long, decltype(T::st_atime_nsec), decltype(T::st_mtime_nsec)>> {
static std::pair<decltype(T::st_atime_nsec), decltype(T::st_mtime_nsec)> get(const T &s) {
return {s.st_atime_nsec, s.st_mtime_nsec};
}
};
Stat from_native_stat(const struct ::stat &buf) {
auto time_nsec = TimeNsec<struct ::stat>::get(buf);
Stat res;
res.atime_nsec_ = static_cast<uint64>(buf.st_atime) * 1000000000 + time_nsec.first;
res.mtime_nsec_ = static_cast<uint64>(buf.st_mtime) * 1000000000 + time_nsec.second / 1000 * 1000;
res.size_ = buf.st_size;
res.is_dir_ = (buf.st_mode & S_IFMT) == S_IFDIR;
res.is_reg_ = (buf.st_mode & S_IFMT) == S_IFREG;
return res;
}
Result<Stat> fstat(int native_fd) {
struct ::stat buf;
if (detail::skip_eintr([&] { return ::fstat(native_fd, &buf); }) < 0) {
return OS_ERROR(PSLICE() << "Stat for fd " << native_fd << " failed");
}
return detail::from_native_stat(buf);
}
Status update_atime(int native_fd) {
#if TD_LINUX
timespec times[2];
// access time
times[0].tv_nsec = UTIME_NOW;
times[0].tv_sec = 0;
// modify time
times[1].tv_nsec = UTIME_OMIT;
times[1].tv_sec = 0;
if (futimens(native_fd, times) < 0) {
auto status = OS_ERROR(PSLICE() << "futimens " << tag("fd", native_fd));
LOG(WARNING) << status;
return status;
}
return Status::OK();
#elif TD_DARWIN
TRY_RESULT(info, fstat(native_fd));
timeval upd[2];
auto now = Clocks::system();
// access time
upd[0].tv_sec = static_cast<decltype(upd[0].tv_sec)>(now);
upd[0].tv_usec = static_cast<decltype(upd[0].tv_usec)>((now - static_cast<double>(upd[0].tv_sec)) * 1000000);
// modify time
upd[1].tv_sec = static_cast<decltype(upd[1].tv_sec)>(info.mtime_nsec_ / 1000000000ll);
upd[1].tv_usec = static_cast<decltype(upd[1].tv_usec)>(info.mtime_nsec_ % 1000000000ll / 1000);
if (futimes(native_fd, upd) < 0) {
auto status = OS_ERROR(PSLICE() << "futimes " << tag("fd", native_fd));
LOG(WARNING) << status;
return status;
}
return Status::OK();
#else
return Status::Error("Not supported");
// timespec times[2];
//// access time
// times[0].tv_nsec = UTIME_NOW;
//// modify time
// times[1].tv_nsec = UTIME_OMIT;
//// int err = syscall(__NR_utimensat, native_fd, nullptr, times, 0);
// if (futimens(native_fd, times) < 0) {
// auto status = OS_ERROR(PSLICE() << "futimens " << tag("fd", native_fd));
// LOG(WARNING) << status;
// return status;
// }
// return Status::OK();
#endif
}
} // namespace detail
Status update_atime(CSlice path) {
TRY_RESULT(file, FileFd::open(path, FileFd::Flags::Read));
SCOPE_EXIT {
file.close();
};
return detail::update_atime(file.get_native_fd().fd());
}
Result<Stat> stat(CSlice path) {
struct ::stat buf;
int err = detail::skip_eintr([&] { return ::stat(path.c_str(), &buf); });
if (err < 0) {
return OS_ERROR(PSLICE() << "Stat for file \"" << path << "\" failed");
}
return detail::from_native_stat(buf);
}
Result<MemStat> mem_stat() {
#if TD_DARWIN
task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (KERN_SUCCESS !=
task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&t_info), &t_info_count)) {
return Status::Error("task_info failed");
}
MemStat res;
res.resident_size_ = t_info.resident_size;
res.virtual_size_ = t_info.virtual_size;
res.resident_size_peak_ = 0;
res.virtual_size_peak_ = 0;
return res;
#elif TD_LINUX || TD_ANDROID || TD_TIZEN
TRY_RESULT(fd, FileFd::open("/proc/self/status", FileFd::Read));
SCOPE_EXIT {
fd.close();
};
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
CHECK(size < TMEM_SIZE - 1);
mem[size] = 0;
const char *s = mem;
MemStat res;
while (*s) {
const char *name_begin = s;
while (*s != 0 && *s != '\n') {
s++;
}
auto name_end = name_begin;
while (is_alpha(*name_end)) {
name_end++;
}
Slice name(name_begin, name_end);
uint64 *x = nullptr;
if (name == "VmPeak") {
x = &res.virtual_size_peak_;
}
if (name == "VmSize") {
x = &res.virtual_size_;
}
if (name == "VmHWM") {
x = &res.resident_size_peak_;
}
if (name == "VmRSS") {
x = &res.resident_size_;
}
if (x != nullptr) {
Slice value(name_end, s);
if (!value.empty() && value[0] == ':') {
value.remove_prefix(1);
}
value = trim(value);
value = split(value).first;
auto r_mem = to_integer_safe<uint64>(value);
if (r_mem.is_error()) {
LOG(ERROR) << "Failed to parse memory stats " << tag("name", name) << tag("value", value);
*x = static_cast<uint64>(-1);
} else {
*x = r_mem.ok() * 1024; // memory is in kB
}
}
if (*s == 0) {
break;
}
s++;
}
return res;
#else
return Status::Error("Not supported");
#endif
}
#if TD_LINUX
Status cpu_stat_self(CpuStat &stat) {
TRY_RESULT(fd, FileFd::open("/proc/self/stat", FileFd::Read));
SCOPE_EXIT {
fd.close();
};
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
CHECK(size < TMEM_SIZE - 1);
mem[size] = 0;
char *s = mem;
char *t = mem + size;
int pass_cnt = 0;
while (pass_cnt < 15) {
if (pass_cnt == 13) {
stat.process_user_ticks = to_integer<uint64>(Slice(s, t));
}
if (pass_cnt == 14) {
stat.process_system_ticks = to_integer<uint64>(Slice(s, t));
}
while (*s && *s != ' ') {
s++;
}
if (*s == ' ') {
s++;
pass_cnt++;
} else {
return Status::Error("unexpected end of proc file");
}
}
return Status::OK();
}
Status cpu_stat_total(CpuStat &stat) {
TRY_RESULT(fd, FileFd::open("/proc/stat", FileFd::Read));
SCOPE_EXIT {
fd.close();
};
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
CHECK(size < TMEM_SIZE - 1);
mem[size] = 0;
uint64 sum = 0, cur = 0;
for (size_t i = 0; i < size; i++) {
int c = mem[i];
if (c >= '0' && c <= '9') {
cur = cur * 10 + (uint64)c - '0';
} else {
sum += cur;
cur = 0;
if (c == '\n') {
break;
}
}
}
stat.total_ticks = sum;
return Status::OK();
}
#endif
Result<CpuStat> cpu_stat() {
#if TD_LINUX
CpuStat stat;
TRY_STATUS(cpu_stat_self(stat));
TRY_STATUS(cpu_stat_total(stat));
return stat;
#else
return Status::Error("Not supported");
#endif
}
} // namespace td
#endif
#if TD_PORT_WINDOWS
namespace td {
Result<Stat> stat(CSlice path) {
TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
return fd.stat();
}
Result<CpuStat> cpu_stat() {
return Status::Error("Not supported");
}
} // namespace td
#endif

View file

@ -0,0 +1,65 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
struct Stat {
bool is_dir_;
bool is_reg_;
int64 size_;
uint64 atime_nsec_;
uint64 mtime_nsec_;
};
Result<Stat> stat(CSlice path) TD_WARN_UNUSED_RESULT;
struct CpuStat {
uint64 total_ticks{0};
uint64 process_user_ticks{0};
uint64 process_system_ticks{0};
};
Result<CpuStat> cpu_stat() TD_WARN_UNUSED_RESULT;
#if TD_PORT_POSIX
namespace detail {
Result<Stat> fstat(int native_fd);
} // namespace detail
Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
struct MemStat {
uint64 resident_size_ = 0;
uint64 resident_size_peak_ = 0;
uint64 virtual_size_ = 0;
uint64 virtual_size_peak_ = 0;
};
Result<MemStat> mem_stat() TD_WARN_UNUSED_RESULT;
#endif
} // namespace td

View file

@ -0,0 +1,250 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/StdStreams.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/thread.h"
#include "td/utils/Slice.h"
#include <atomic>
namespace td {
#if TD_PORT_POSIX
template <int id>
static FileFd &get_file_fd() {
static FileFd result = FileFd::from_native_fd(NativeFd(id, true));
static auto guard = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
return result;
}
FileFd &Stdin() {
return get_file_fd<0>();
}
FileFd &Stdout() {
return get_file_fd<1>();
}
FileFd &Stderr() {
return get_file_fd<2>();
}
#elif TD_PORT_WINDOWS
template <DWORD id>
static FileFd &get_file_fd() {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
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 = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
#else
static FileFd result;
#endif
return result;
}
FileFd &Stdin() {
return get_file_fd<STD_INPUT_HANDLE>();
}
FileFd &Stdout() {
return get_file_fd<STD_OUTPUT_HANDLE>();
}
FileFd &Stderr() {
return get_file_fd<STD_ERROR_HANDLE>();
}
#endif
#if TD_PORT_WINDOWS
namespace detail {
class BufferedStdinImpl : public Iocp::Callback {
public:
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
BufferedStdinImpl() : info_(NativeFd(GetStdHandle(STD_INPUT_HANDLE), true)) {
iocp_ref_ = Iocp::get()->get_ref();
read_thread_ = td::thread([this] { this->read_loop(); });
}
#else
BufferedStdinImpl() {
close();
}
#endif
BufferedStdinImpl(const BufferedStdinImpl &) = delete;
BufferedStdinImpl &operator=(const BufferedStdinImpl &) = delete;
BufferedStdinImpl(BufferedStdinImpl &&) = delete;
BufferedStdinImpl &operator=(BufferedStdinImpl &&) = delete;
~BufferedStdinImpl() {
info_.move_as_native_fd().release();
}
void close() {
close_flag_ = true;
}
ChainBufferReader &input_buffer() {
return reader_;
}
PollableFdInfo &get_poll_info() {
return info_;
}
const PollableFdInfo &get_poll_info() const {
return info_;
}
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT {
info_.get_flags();
info_.clear_flags(PollFlags::Read());
reader_.sync_with_writer();
return reader_.size();
}
private:
PollableFdInfo info_;
ChainBufferWriter writer_;
ChainBufferReader reader_ = writer_.extract_reader();
td::thread read_thread_;
std::atomic<bool> close_flag_{false};
IocpRef iocp_ref_;
std::atomic<int> refcnt_{1};
void read_loop() {
while (!close_flag_) {
auto slice = writer_.prepare_append();
auto r_size = read(slice);
if (r_size.is_error()) {
LOG(ERROR) << "Stop read stdin loop: " << r_size.error();
break;
}
writer_.confirm_append(r_size.ok());
if (iocp_ref_.post(0, this, nullptr)) {
inc_refcnt();
}
}
if (!iocp_ref_.post(0, this, nullptr)) {
dec_refcnt();
}
}
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
info_.add_flags_from_poll(td::PollFlags::Read());
dec_refcnt();
}
bool dec_refcnt() {
if (--refcnt_ == 0) {
delete this;
return true;
}
return false;
}
void inc_refcnt() {
CHECK(refcnt_ != 0);
refcnt_++;
}
Result<size_t> read(MutableSlice slice) {
auto native_fd = info_.native_fd().fd();
DWORD bytes_read = 0;
auto res = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, nullptr);
if (res) {
return static_cast<size_t>(bytes_read);
}
return OS_ERROR(PSLICE() << "Read from " << info_.native_fd() << " has failed");
}
};
void BufferedStdinImplDeleter::operator()(BufferedStdinImpl *impl) {
// LOG(ERROR) << "Close";
impl->close();
}
} // namespace detail
#elif TD_PORT_POSIX
namespace detail {
class BufferedStdinImpl {
public:
BufferedStdinImpl() {
file_fd_ = FileFd::from_native_fd(NativeFd(Stdin().get_native_fd().fd()));
file_fd_.get_native_fd().set_is_blocking(false);
}
BufferedStdinImpl(const BufferedStdinImpl &) = delete;
BufferedStdinImpl &operator=(const BufferedStdinImpl &) = delete;
BufferedStdinImpl(BufferedStdinImpl &&) = delete;
BufferedStdinImpl &operator=(BufferedStdinImpl &&) = delete;
~BufferedStdinImpl() {
file_fd_.get_native_fd().set_is_blocking(true);
file_fd_.move_as_native_fd().release();
}
ChainBufferReader &input_buffer() {
return reader_;
}
PollableFdInfo &get_poll_info() {
return file_fd_.get_poll_info();
}
const PollableFdInfo &get_poll_info() const {
return file_fd_.get_poll_info();
}
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT {
size_t result = 0;
while (::td::can_read(*this) && max_read) {
MutableSlice slice = writer_.prepare_append().truncate(max_read);
TRY_RESULT(x, file_fd_.read(slice));
slice.truncate(x);
writer_.confirm_append(x);
result += x;
max_read -= x;
}
if (result) {
reader_.sync_with_writer();
}
return result;
}
private:
FileFd file_fd_;
ChainBufferWriter writer_;
ChainBufferReader reader_ = writer_.extract_reader();
};
void BufferedStdinImplDeleter::operator()(BufferedStdinImpl *impl) {
delete impl;
}
} // namespace detail
#endif
BufferedStdin::BufferedStdin() : impl_(make_unique<detail::BufferedStdinImpl>().release()) {
}
BufferedStdin::BufferedStdin(BufferedStdin &&) = default;
BufferedStdin &BufferedStdin::operator=(BufferedStdin &&) = default;
BufferedStdin::~BufferedStdin() = default;
ChainBufferReader &BufferedStdin::input_buffer() {
return impl_->input_buffer();
}
PollableFdInfo &BufferedStdin::get_poll_info() {
return impl_->get_poll_info();
}
const PollableFdInfo &BufferedStdin::get_poll_info() const {
return impl_->get_poll_info();
}
Result<size_t> BufferedStdin::flush_read(size_t max_read) {
return impl_->flush_read(max_read);
}
} // namespace td

View file

@ -0,0 +1,61 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Status.h"
#include <limits>
#include <memory>
namespace td {
FileFd &Stdin();
FileFd &Stdout();
FileFd &Stderr();
namespace detail {
class BufferedStdinImpl;
class BufferedStdinImplDeleter {
public:
void operator()(BufferedStdinImpl *impl);
};
} // namespace detail
class BufferedStdin {
public:
BufferedStdin();
BufferedStdin(const BufferedStdin &) = delete;
BufferedStdin &operator=(const BufferedStdin &) = delete;
BufferedStdin(BufferedStdin &&);
BufferedStdin &operator=(BufferedStdin &&);
~BufferedStdin();
ChainBufferReader &input_buffer();
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
private:
std::unique_ptr<detail::BufferedStdinImpl, detail::BufferedStdinImplDeleter> impl_;
};
} // namespace td

View file

@ -0,0 +1,873 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/UdpSocketFd.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/VectorQueue.h"
#if TD_PORT_WINDOWS
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/SpinLock.h"
#endif
#if TD_PORT_POSIX
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#if TD_LINUX
#include <linux/errqueue.h>
#endif
#endif // TD_PORT_POSIX
#include <array>
#include <atomic>
#include <cstring>
namespace td {
namespace detail {
#if TD_PORT_WINDOWS
class UdpSocketReceiveHelper {
public:
void to_native(const UdpMessage &message, WSAMSG &message_header) {
socklen_t addr_len{narrow_cast<socklen_t>(sizeof(addr_))};
message_header.name = reinterpret_cast<struct sockaddr *>(&addr_);
message_header.namelen = addr_len;
buf_.buf = const_cast<char *>(message.data.as_slice().begin());
buf_.len = narrow_cast<DWORD>(message.data.size());
message_header.lpBuffers = &buf_;
message_header.dwBufferCount = 1;
message_header.Control.buf = nullptr; // control_buf_.data();
message_header.Control.len = 0; // narrow_cast<decltype(message_header.Control.len)>(control_buf_.size());
message_header.dwFlags = 0;
}
void from_native(WSAMSG &message_header, size_t message_size, UdpMessage &message) {
message.address.init_sockaddr(reinterpret_cast<struct sockaddr *>(message_header.name), message_header.namelen)
.ignore();
message.error = Status::OK();
if ((message_header.dwFlags & (MSG_TRUNC | MSG_CTRUNC)) != 0) {
message.error = Status::Error(501, "message too long");
message.data = BufferSlice();
return;
}
CHECK(message_size <= message.data.size());
message.data.truncate(message_size);
CHECK(message_size == message.data.size());
}
private:
std::array<char, 1024> control_buf_;
sockaddr_storage addr_;
WSABUF buf_;
};
class UdpSocketSendHelper {
public:
void to_native(const UdpMessage &message, WSAMSG &message_header) {
message_header.name = const_cast<struct sockaddr *>(message.address.get_sockaddr());
message_header.namelen = narrow_cast<socklen_t>(message.address.get_sockaddr_len());
buf_.buf = const_cast<char *>(message.data.as_slice().begin());
buf_.len = narrow_cast<DWORD>(message.data.size());
message_header.lpBuffers = &buf_;
message_header.dwBufferCount = 1;
message_header.Control.buf = nullptr;
message_header.Control.len = 0;
message_header.dwFlags = 0;
}
private:
WSABUF buf_;
};
class UdpSocketFdImpl : private Iocp::Callback {
public:
explicit UdpSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
get_poll_info().add_flags(PollFlags::Write());
Iocp::get()->subscribe(get_native_fd(), this);
is_receive_active_ = true;
notify_iocp_connected();
}
PollableFdInfo &get_poll_info() {
return info_;
}
const PollableFdInfo &get_poll_info() const {
return info_;
}
const NativeFd &get_native_fd() const {
return info_.native_fd();
}
void close() {
notify_iocp_close();
}
Result<optional<UdpMessage>> receive() {
auto lock = lock_.lock();
if (!pending_errors_.empty()) {
auto status = pending_errors_.pop();
if (!UdpSocketFd::is_critical_read_error(status)) {
return UdpMessage{{}, {}, std::move(status)};
}
return std::move(status);
}
if (!receive_queue_.empty()) {
return receive_queue_.pop();
}
return optional<UdpMessage>{};
}
void send(UdpMessage message) {
auto lock = lock_.lock();
send_queue_.push(std::move(message));
}
Status flush_send() {
if (is_send_waiting_) {
auto lock = lock_.lock();
is_send_waiting_ = false;
notify_iocp_send();
}
return Status::OK();
}
private:
PollableFdInfo info_;
SpinLock lock_;
std::atomic<int> refcnt_{1};
bool is_connected_{false};
bool close_flag_{false};
bool is_send_active_{false};
bool is_send_waiting_{false};
VectorQueue<UdpMessage> send_queue_;
WSAOVERLAPPED send_overlapped_;
bool is_receive_active_{false};
VectorQueue<UdpMessage> receive_queue_;
VectorQueue<Status> pending_errors_;
UdpMessage to_receive_;
WSAMSG receive_message_;
UdpSocketReceiveHelper receive_helper_;
enum : size_t { MAX_PACKET_SIZE = 2048, RESERVED_SIZE = MAX_PACKET_SIZE * 8 };
BufferSlice receive_buffer_;
UdpMessage to_send_;
WSAOVERLAPPED receive_overlapped_;
char close_overlapped_;
bool check_status(Slice message) {
auto last_error = WSAGetLastError();
if (last_error == ERROR_IO_PENDING) {
return true;
}
on_error(OS_SOCKET_ERROR(message));
return false;
}
void loop_receive() {
CHECK(!is_receive_active_);
if (close_flag_) {
return;
}
std::memset(&receive_overlapped_, 0, sizeof(receive_overlapped_));
if (receive_buffer_.size() < MAX_PACKET_SIZE) {
receive_buffer_ = BufferSlice(RESERVED_SIZE);
}
to_receive_.data = receive_buffer_.clone();
receive_helper_.to_native(to_receive_, receive_message_);
LPFN_WSARECVMSG WSARecvMsgPtr = nullptr;
GUID guid = WSAID_WSARECVMSG;
DWORD numBytes;
auto error = ::WSAIoctl(get_native_fd().socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, static_cast<void *>(&guid),
sizeof(guid), static_cast<void *>(&WSARecvMsgPtr), sizeof(WSARecvMsgPtr), &numBytes,
nullptr, nullptr);
if (error) {
on_error(OS_SOCKET_ERROR("WSAIoctl failed"));
return;
}
auto status = WSARecvMsgPtr(get_native_fd().socket(), &receive_message_, nullptr, &receive_overlapped_, nullptr);
if (status == 0 || check_status("WSARecvMsg failed")) {
inc_refcnt();
is_receive_active_ = true;
}
}
void loop_send() {
CHECK(!is_send_active_);
{
auto lock = lock_.lock();
if (send_queue_.empty()) {
is_send_waiting_ = true;
return;
}
to_send_ = send_queue_.pop();
}
std::memset(&send_overlapped_, 0, sizeof(send_overlapped_));
WSAMSG message;
UdpSocketSendHelper send_helper;
send_helper.to_native(to_send_, message);
auto status = WSASendMsg(get_native_fd().socket(), &message, 0, nullptr, &send_overlapped_, nullptr);
if (status == 0 || check_status("WSASendMsg failed")) {
inc_refcnt();
is_send_active_ = true;
}
}
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
// called from other thread
if (dec_refcnt() || close_flag_) {
VLOG(fd) << "Ignore IOCP (UDP socket is closing)";
return;
}
if (r_size.is_error()) {
return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
}
if (!is_connected_ && overlapped == &receive_overlapped_) {
return on_connected();
}
auto size = r_size.move_as_ok();
if (overlapped == &send_overlapped_) {
return on_send(size);
}
if (overlapped == nullptr) {
CHECK(size == 0);
return on_send(size);
}
if (overlapped == &receive_overlapped_) {
return on_receive(size);
}
if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
return on_close();
}
UNREACHABLE();
}
void on_error(Status status) {
VLOG(fd) << get_native_fd() << " on error " << status;
{
auto lock = lock_.lock();
pending_errors_.push(std::move(status));
}
get_poll_info().add_flags_from_poll(PollFlags::Error());
}
void on_connected() {
VLOG(fd) << get_native_fd() << " on connected";
CHECK(!is_connected_);
CHECK(is_receive_active_);
is_connected_ = true;
is_receive_active_ = false;
loop_receive();
loop_send();
}
void on_receive(size_t size) {
VLOG(fd) << get_native_fd() << " on receive " << size;
CHECK(is_receive_active_);
is_receive_active_ = false;
receive_helper_.from_native(receive_message_, size, to_receive_);
receive_buffer_.confirm_read((to_receive_.data.size() + 7) & ~7);
{
auto lock = lock_.lock();
// LOG(ERROR) << format::escaped(to_receive_.data.as_slice());
receive_queue_.push(std::move(to_receive_));
}
get_poll_info().add_flags_from_poll(PollFlags::Read());
loop_receive();
}
void on_send(size_t size) {
VLOG(fd) << get_native_fd() << " on send " << size;
if (size == 0) {
if (is_send_active_) {
return;
}
is_send_active_ = true;
}
CHECK(is_send_active_);
is_send_active_ = false;
loop_send();
}
void on_close() {
VLOG(fd) << get_native_fd() << " on close";
close_flag_ = true;
info_.set_native_fd({});
}
bool dec_refcnt() {
if (--refcnt_ == 0) {
delete this;
return true;
}
return false;
}
void inc_refcnt() {
CHECK(refcnt_ != 0);
refcnt_++;
}
void notify_iocp_send() {
inc_refcnt();
Iocp::get()->post(0, this, nullptr);
}
void notify_iocp_close() {
Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
}
void notify_iocp_connected() {
inc_refcnt();
Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&receive_overlapped_));
}
};
void UdpSocketFdImplDeleter::operator()(UdpSocketFdImpl *impl) {
impl->close();
}
#elif TD_PORT_POSIX
//struct iovec { [> Scatter/gather array items <]
// void *iov_base; [> Starting address <]
// size_t iov_len; [> Number of bytes to transfer <]
//};
//struct msghdr {
// void *msg_name; [> optional address <]
// socklen_t msg_namelen; [> size of address <]
// struct iovec *msg_iov; [> scatter/gather array <]
// size_t msg_iovlen; [> # elements in msg_iov <]
// void *msg_control; [> ancillary data, see below <]
// size_t msg_controllen; [> ancillary data buffer len <]
// int msg_flags; [> flags on received message <]
//};
class UdpSocketReceiveHelper {
public:
void to_native(const UdpSocketFd::InboundMessage &message, struct msghdr &message_header) {
socklen_t addr_len{narrow_cast<socklen_t>(sizeof(addr_))};
message_header.msg_name = &addr_;
message_header.msg_namelen = addr_len;
io_vec_.iov_base = message.data.begin();
io_vec_.iov_len = message.data.size();
message_header.msg_iov = &io_vec_;
message_header.msg_iovlen = 1;
message_header.msg_control = control_buf_.data();
message_header.msg_controllen = narrow_cast<decltype(message_header.msg_controllen)>(control_buf_.size());
message_header.msg_flags = 0;
}
void from_native(struct msghdr &message_header, size_t message_size, UdpSocketFd::InboundMessage &message) {
#if TD_LINUX
struct cmsghdr *cmsg;
struct sock_extended_err *ee = nullptr;
for (cmsg = CMSG_FIRSTHDR(&message_header); cmsg != nullptr; cmsg = CMSG_NXTHDR(&message_header, cmsg)) {
if (cmsg->cmsg_type == IP_PKTINFO && cmsg->cmsg_level == IPPROTO_IP) {
//auto *pi = reinterpret_cast<struct in_pktinfo *>(CMSG_DATA(cmsg));
} else if (cmsg->cmsg_type == IPV6_PKTINFO && cmsg->cmsg_level == IPPROTO_IPV6) {
//auto *pi = reinterpret_cast<struct in6_pktinfo *>(CMSG_DATA(cmsg));
} else if ((cmsg->cmsg_type == IP_RECVERR && cmsg->cmsg_level == IPPROTO_IP) ||
(cmsg->cmsg_type == IPV6_RECVERR && cmsg->cmsg_level == IPPROTO_IPV6)) {
ee = reinterpret_cast<struct sock_extended_err *>(CMSG_DATA(cmsg));
}
}
if (ee != nullptr) {
auto *addr = reinterpret_cast<struct sockaddr *>(SO_EE_OFFENDER(ee));
IPAddress address;
address.init_sockaddr(addr).ignore();
if (message.from != nullptr) {
*message.from = address;
}
if (message.error) {
*message.error = Status::PosixError(ee->ee_errno, "");
}
//message.data = MutableSlice();
message.data.truncate(0);
return;
}
#endif
if (message.from != nullptr) {
message.from
->init_sockaddr(reinterpret_cast<struct sockaddr *>(message_header.msg_name), message_header.msg_namelen)
.ignore();
}
if (message.error) {
*message.error = Status::OK();
}
if (message_header.msg_flags & MSG_TRUNC) {
if (message.error) {
*message.error = Status::Error(501, "message too long");
}
message.data.truncate(0);
return;
}
CHECK(message_size <= message.data.size());
message.data.truncate(message_size);
CHECK(message_size == message.data.size());
}
private:
std::array<char, 1024> control_buf_;
sockaddr_storage addr_;
struct iovec io_vec_;
};
class UdpSocketSendHelper {
public:
void to_native(const UdpSocketFd::OutboundMessage &message, struct msghdr &message_header) {
CHECK(message.to != nullptr && message.to->is_valid());
message_header.msg_name = const_cast<struct sockaddr *>(message.to->get_sockaddr());
message_header.msg_namelen = narrow_cast<socklen_t>(message.to->get_sockaddr_len());
io_vec_.iov_base = const_cast<char *>(message.data.begin());
io_vec_.iov_len = message.data.size();
message_header.msg_iov = &io_vec_;
message_header.msg_iovlen = 1;
//TODO
message_header.msg_control = nullptr;
message_header.msg_controllen = 0;
message_header.msg_flags = 0;
}
private:
struct iovec io_vec_;
};
class UdpSocketFdImpl {
public:
explicit UdpSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
}
PollableFdInfo &get_poll_info() {
return info_;
}
const PollableFdInfo &get_poll_info() const {
return info_;
}
const NativeFd &get_native_fd() const {
return info_.native_fd();
}
Status get_pending_error() {
if (!get_poll_info().get_flags().has_pending_error()) {
return Status::OK();
}
TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
get_poll_info().clear_flags(PollFlags::Error());
return Status::OK();
}
Status receive_message(UdpSocketFd::InboundMessage &message, bool &is_received) {
is_received = false;
int flags = 0;
if (get_poll_info().get_flags().has_pending_error()) {
#ifdef MSG_ERRQUEUE
flags = MSG_ERRQUEUE;
#else
return get_pending_error();
#endif
}
struct msghdr message_header;
detail::UdpSocketReceiveHelper helper;
helper.to_native(message, message_header);
auto native_fd = get_native_fd().socket();
auto recvmsg_res = detail::skip_eintr([&] { return recvmsg(native_fd, &message_header, flags); });
auto recvmsg_errno = errno;
if (recvmsg_res >= 0) {
helper.from_native(message_header, recvmsg_res, message);
is_received = true;
return Status::OK();
}
return process_recvmsg_error(recvmsg_errno, is_received);
}
Status process_recvmsg_error(int recvmsg_errno, bool &is_received) {
is_received = false;
if (recvmsg_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| recvmsg_errno == EWOULDBLOCK
#endif
) {
if (get_poll_info().get_flags_local().has_pending_error()) {
get_poll_info().clear_flags(PollFlags::Error());
} else {
get_poll_info().clear_flags(PollFlags::Read());
}
return Status::OK();
}
auto error = Status::PosixError(recvmsg_errno, PSLICE() << "Receive from " << get_native_fd() << " has failed");
switch (recvmsg_errno) {
case EBADF:
case EFAULT:
case EINVAL:
case ENOTCONN:
case ECONNRESET:
case ETIMEDOUT:
LOG(FATAL) << error;
UNREACHABLE();
default:
LOG(WARNING) << "Unknown error: " << error;
// fallthrough
case ENOBUFS:
case ENOMEM:
#ifdef MSG_ERRQUEUE
get_poll_info().add_flags(PollFlags::Error());
#endif
return error;
}
}
Status send_message(const UdpSocketFd::OutboundMessage &message, bool &is_sent) {
is_sent = false;
struct msghdr message_header;
detail::UdpSocketSendHelper helper;
helper.to_native(message, message_header);
auto native_fd = get_native_fd().socket();
auto sendmsg_res = detail::skip_eintr([&] { return sendmsg(native_fd, &message_header, 0); });
auto sendmsg_errno = errno;
if (sendmsg_res >= 0) {
is_sent = true;
return Status::OK();
}
return process_sendmsg_error(sendmsg_errno, is_sent);
}
Status process_sendmsg_error(int sendmsg_errno, bool &is_sent) {
if (sendmsg_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| sendmsg_errno == EWOULDBLOCK
#endif
) {
get_poll_info().clear_flags(PollFlags::Write());
return Status::OK();
}
auto error = Status::PosixError(sendmsg_errno, PSLICE() << "Send from " << get_native_fd() << " has failed");
switch (sendmsg_errno) {
// Still may send some other packets, but there is no point to resend this particular message
case EACCES:
case EMSGSIZE:
case EPERM:
LOG(WARNING) << "Silently drop packet :( " << error;
//TODO: get errors from MSG_ERRQUEUE is possible
is_sent = true;
return error;
// Some general problems, which may be fixed in future
case ENOMEM:
case EDQUOT:
case EFBIG:
case ENETDOWN:
case ENETUNREACH:
case ENOSPC:
case EHOSTUNREACH:
case ENOBUFS:
default:
#ifdef MSG_ERRQUEUE
get_poll_info().add_flags(PollFlags::Error());
#endif
return error;
case EBADF: // impossible
case ENOTSOCK: // impossible
case EPIPE: // impossible for udp
case ECONNRESET: // impossible for udp
case EDESTADDRREQ: // we checked that address is valid
case ENOTCONN: // we checked that address is valid
case EINTR: // we already skipped all EINTR
case EISCONN: // impossible for udp socket
case EOPNOTSUPP:
case ENOTDIR:
case EFAULT:
case EINVAL:
case EAFNOSUPPORT:
LOG(FATAL) << error;
UNREACHABLE();
return error;
}
}
Status send_messages(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
#if TD_HAS_MMSG
return send_messages_fast(messages, cnt);
#else
return send_messages_slow(messages, cnt);
#endif
}
Status receive_messages(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
#if TD_HAS_MMSG
return receive_messages_fast(messages, cnt);
#else
return receive_messages_slow(messages, cnt);
#endif
}
private:
PollableFdInfo info_;
Status send_messages_slow(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
cnt = 0;
for (auto &message : messages) {
CHECK(!message.data.empty());
bool is_sent;
auto error = send_message(message, is_sent);
cnt += is_sent;
TRY_STATUS(std::move(error));
}
return Status::OK();
}
#if TD_HAS_MMSG
Status send_messages_fast(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
//struct mmsghdr {
// struct msghdr msg_hdr; [> Message header <]
// unsigned int msg_len; [> Number of bytes transmitted <]
//};
struct std::array<detail::UdpSocketSendHelper, 16> helpers;
struct std::array<struct mmsghdr, 16> headers;
size_t to_send = min(messages.size(), headers.size());
for (size_t i = 0; i < to_send; i++) {
helpers[i].to_native(messages[i], headers[i].msg_hdr);
headers[i].msg_len = 0;
}
auto native_fd = get_native_fd().socket();
auto sendmmsg_res =
detail::skip_eintr([&] { return sendmmsg(native_fd, headers.data(), narrow_cast<unsigned int>(to_send), 0); });
auto sendmmsg_errno = errno;
if (sendmmsg_res >= 0) {
cnt = sendmmsg_res;
return Status::OK();
}
bool is_sent = false;
auto status = process_sendmsg_error(sendmmsg_errno, is_sent);
cnt = is_sent;
return status;
}
#endif
Status receive_messages_slow(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
cnt = 0;
while (cnt < messages.size() && get_poll_info().get_flags().can_read()) {
auto &message = messages[cnt];
CHECK(!message.data.empty());
bool is_received;
auto error = receive_message(message, is_received);
cnt += is_received;
TRY_STATUS(std::move(error));
}
return Status::OK();
}
#if TD_HAS_MMSG
Status receive_messages_fast(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
int flags = 0;
cnt = 0;
if (get_poll_info().get_flags().has_pending_error()) {
#ifdef MSG_ERRQUEUE
flags = MSG_ERRQUEUE;
#else
return get_pending_error();
#endif
}
//struct mmsghdr {
// struct msghdr msg_hdr; [> Message header <]
// unsigned int msg_len; [> Number of bytes transmitted <]
//};
struct std::array<detail::UdpSocketReceiveHelper, 16> helpers;
struct std::array<struct mmsghdr, 16> headers;
size_t to_receive = min(messages.size(), headers.size());
for (size_t i = 0; i < to_receive; i++) {
helpers[i].to_native(messages[i], headers[i].msg_hdr);
headers[i].msg_len = 0;
}
auto native_fd = get_native_fd().socket();
auto recvmmsg_res = detail::skip_eintr(
[&] { return recvmmsg(native_fd, headers.data(), narrow_cast<unsigned int>(to_receive), flags, nullptr); });
auto recvmmsg_errno = errno;
if (recvmmsg_res >= 0) {
cnt = narrow_cast<size_t>(recvmmsg_res);
for (size_t i = 0; i < cnt; i++) {
helpers[i].from_native(headers[i].msg_hdr, headers[i].msg_len, messages[i]);
}
return Status::OK();
}
bool is_received;
auto status = process_recvmsg_error(recvmmsg_errno, is_received);
cnt = is_received;
return status;
}
#endif
};
void UdpSocketFdImplDeleter::operator()(UdpSocketFdImpl *impl) {
delete impl;
}
#endif
} // namespace detail
UdpSocketFd::UdpSocketFd() = default;
UdpSocketFd::UdpSocketFd(UdpSocketFd &&) = default;
UdpSocketFd &UdpSocketFd::operator=(UdpSocketFd &&) = default;
UdpSocketFd::~UdpSocketFd() = default;
PollableFdInfo &UdpSocketFd::get_poll_info() {
return impl_->get_poll_info();
}
const PollableFdInfo &UdpSocketFd::get_poll_info() const {
return impl_->get_poll_info();
}
Result<UdpSocketFd> UdpSocketFd::open(const IPAddress &address) {
NativeFd native_fd{socket(address.get_address_family(), SOCK_DGRAM, IPPROTO_UDP)};
if (!native_fd) {
return OS_SOCKET_ERROR("Failed to create a socket");
}
TRY_STATUS(native_fd.set_is_blocking_unsafe(false));
auto sock = native_fd.socket();
#if TD_PORT_POSIX
int flags = 1;
#elif TD_PORT_WINDOWS
BOOL flags = TRUE;
#endif
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
// TODO: SO_REUSEADDR, SO_KEEPALIVE, TCP_NODELAY, SO_SNDBUF, SO_RCVBUF, TCP_QUICKACK, SO_LINGER
auto bind_addr = address.get_any_addr();
bind_addr.set_port(address.get_port());
auto e_bind = bind(sock, bind_addr.get_sockaddr(), narrow_cast<int>(bind_addr.get_sockaddr_len()));
if (e_bind != 0) {
return OS_SOCKET_ERROR("Failed to bind a socket");
}
return UdpSocketFd(make_unique<detail::UdpSocketFdImpl>(std::move(native_fd)));
}
UdpSocketFd::UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl) : impl_(impl.release()) {
}
void UdpSocketFd::close() {
impl_.reset();
}
bool UdpSocketFd::empty() const {
return !impl_;
}
const NativeFd &UdpSocketFd::get_native_fd() const {
return get_poll_info().native_fd();
}
#if TD_PORT_POSIX
static Result<uint32> maximize_buffer(int socket_fd, int optname, uint32 max) {
/* Start with the default size. */
uint32 old_size;
socklen_t intsize = sizeof(old_size);
if (getsockopt(socket_fd, SOL_SOCKET, optname, &old_size, &intsize)) {
return OS_ERROR("getsockopt() failed");
}
/* Binary-search for the real maximum. */
uint32 last_good = old_size;
uint32 min = old_size;
while (min <= max) {
uint32 avg = min + (max - min) / 2;
if (setsockopt(socket_fd, SOL_SOCKET, optname, &avg, intsize) == 0) {
last_good = avg;
min = avg + 1;
} else {
max = avg - 1;
}
}
return last_good;
}
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {
return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max == 0 ? default_udp_max_snd_buffer_size : max);
}
Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max) {
return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max == 0 ? default_udp_max_rcv_buffer_size : max);
}
#else
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {
return 0;
}
Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max) {
return 0;
}
#endif
#if TD_PORT_POSIX
Status UdpSocketFd::send_message(const OutboundMessage &message, bool &is_sent) {
return impl_->send_message(message, is_sent);
}
Status UdpSocketFd::receive_message(InboundMessage &message, bool &is_received) {
return impl_->receive_message(message, is_received);
}
Status UdpSocketFd::send_messages(Span<OutboundMessage> messages, size_t &count) {
return impl_->send_messages(messages, count);
}
Status UdpSocketFd::receive_messages(MutableSpan<InboundMessage> messages, size_t &count) {
return impl_->receive_messages(messages, count);
}
#endif
#if TD_PORT_WINDOWS
Result<optional<UdpMessage>> UdpSocketFd::receive() {
return impl_->receive();
}
void UdpSocketFd::send(UdpMessage message) {
return impl_->send(std::move(message));
}
Status UdpSocketFd::flush_send() {
return impl_->flush_send();
}
#endif
bool UdpSocketFd::is_critical_read_error(const Status &status) {
return status.code() == ENOMEM || status.code() == ENOBUFS;
}
} // namespace td

View file

@ -0,0 +1,105 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/buffer.h"
#include "td/utils/optional.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
// Udp and errors
namespace detail {
class UdpSocketFdImpl;
class UdpSocketFdImplDeleter {
public:
void operator()(UdpSocketFdImpl *impl);
};
} // namespace detail
struct UdpMessage {
IPAddress address;
BufferSlice data;
Status error;
};
class UdpSocketFd {
public:
UdpSocketFd();
UdpSocketFd(UdpSocketFd &&);
UdpSocketFd &operator=(UdpSocketFd &&);
~UdpSocketFd();
UdpSocketFd(const UdpSocketFd &) = delete;
UdpSocketFd &operator=(const UdpSocketFd &) = delete;
Result<uint32> maximize_snd_buffer(uint32 max_buffer_size = 0);
Result<uint32> maximize_rcv_buffer(uint32 max_buffer_size = 0);
static Result<UdpSocketFd> open(const IPAddress &address) TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
const NativeFd &get_native_fd() const;
void close();
bool empty() const;
static bool is_critical_read_error(const Status &status);
#if TD_PORT_POSIX
struct OutboundMessage {
const IPAddress *to;
Slice data;
};
struct InboundMessage {
IPAddress *from;
MutableSlice data;
Status *error;
};
Status send_message(const OutboundMessage &message, bool &is_sent) TD_WARN_UNUSED_RESULT;
Status receive_message(InboundMessage &message, bool &is_received) TD_WARN_UNUSED_RESULT;
Status send_messages(Span<OutboundMessage> messages, size_t &count) TD_WARN_UNUSED_RESULT;
Status receive_messages(MutableSpan<InboundMessage> messages, size_t &count) TD_WARN_UNUSED_RESULT;
#elif TD_PORT_WINDOWS
Result<optional<UdpMessage> > receive();
void send(UdpMessage message);
Status flush_send();
#endif
private:
static constexpr uint32 default_udp_max_snd_buffer_size = (1 << 24);
static constexpr uint32 default_udp_max_rcv_buffer_size = (1 << 24);
std::unique_ptr<detail::UdpSocketFdImpl, detail::UdpSocketFdImplDeleter> impl_;
explicit UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl);
};
} // namespace td

View file

@ -0,0 +1,65 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/platform.h"
// clang-format off
#if TD_WINDOWS
#define TD_PORT_WINDOWS 1
#else
#define TD_PORT_POSIX 1
#endif
#if TD_LINUX || TD_ANDROID || TD_TIZEN
#define TD_POLL_EPOLL 1
#define TD_EVENTFD_LINUX 1
#elif TD_FREEBSD || TD_OPENBSD || TD_NETBSD
#define TD_POLL_KQUEUE 1
#define TD_EVENTFD_BSD 1
#elif TD_CYGWIN
#define TD_POLL_SELECT 1
#define TD_EVENTFD_BSD 1
#elif TD_EMSCRIPTEN
#define TD_POLL_POLL 1
#define TD_EVENTFD_UNSUPPORTED 1
#elif TD_DARWIN
#define TD_POLL_KQUEUE 1
#define TD_EVENTFD_BSD 1
#elif TD_WINDOWS
#define TD_POLL_WINEVENT 1
#define TD_EVENTFD_WINDOWS 1
#else
#error "Poll's implementation is not defined"
#endif
#if TD_EMSCRIPTEN
#define TD_THREAD_UNSUPPORTED 1
#elif TD_TIZEN || TD_LINUX || TD_DARWIN
#define TD_THREAD_PTHREAD 1
#else
#define TD_THREAD_STL 1
#endif
#if TD_LINUX
#define TD_HAS_MMSG 1
#endif
// clang-format on

View file

@ -0,0 +1,136 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/Epoll.h"
char disable_linker_warning_about_empty_file_epoll_cpp TD_UNUSED;
#ifdef TD_POLL_EPOLL
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <unistd.h>
namespace td {
namespace detail {
void Epoll::init() {
CHECK(!epoll_fd_);
epoll_fd_ = NativeFd(epoll_create(1));
auto epoll_create_errno = errno;
LOG_IF(FATAL, !epoll_fd_) << Status::PosixError(epoll_create_errno, "epoll_create failed");
events_.resize(1000);
}
void Epoll::clear() {
if (!epoll_fd_) {
return;
}
events_.clear();
epoll_fd_.close();
for (auto *list_node = list_root_.next; list_node != &list_root_;) {
auto pollable_fd = PollableFd::from_list_node(list_node);
list_node = list_node->next;
}
}
void Epoll::subscribe(PollableFd fd, PollFlags flags) {
epoll_event event;
event.events = EPOLLHUP | EPOLLERR | EPOLLET;
#ifdef EPOLLRDHUP
event.events |= EPOLLRDHUP;
#endif
if (flags.can_read()) {
event.events |= EPOLLIN;
}
if (flags.can_write()) {
event.events |= EPOLLOUT;
}
auto native_fd = fd.native_fd().fd();
auto *list_node = fd.release_as_list_node();
list_root_.put(list_node);
event.data.ptr = list_node;
int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_ADD, native_fd, &event);
auto epoll_ctl_errno = errno;
LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl ADD failed")
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd;
}
void Epoll::unsubscribe(PollableFdRef fd_ref) {
auto fd = fd_ref.lock();
auto native_fd = fd.native_fd().fd();
int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr);
auto epoll_ctl_errno = errno;
LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed")
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd << fd.native_fd().validate();
}
void Epoll::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
void Epoll::run(int timeout_ms) {
int ready_n = epoll_wait(epoll_fd_.fd(), &events_[0], static_cast<int>(events_.size()), timeout_ms);
auto epoll_wait_errno = errno;
LOG_IF(FATAL, ready_n == -1 && epoll_wait_errno != EINTR)
<< Status::PosixError(epoll_wait_errno, "epoll_wait failed");
for (int i = 0; i < ready_n; i++) {
PollFlags flags;
epoll_event *event = &events_[i];
if (event->events & EPOLLIN) {
event->events &= ~EPOLLIN;
flags = flags | PollFlags::Read();
}
if (event->events & EPOLLOUT) {
event->events &= ~EPOLLOUT;
flags = flags | PollFlags::Write();
}
#ifdef EPOLLRDHUP
if (event->events & EPOLLRDHUP) {
event->events &= ~EPOLLRDHUP;
// flags |= Fd::Close;
// TODO
}
#endif
if (event->events & EPOLLHUP) {
event->events &= ~EPOLLHUP;
flags = flags | PollFlags::Close();
}
if (event->events & EPOLLERR) {
event->events &= ~EPOLLERR;
flags = flags | PollFlags::Error();
}
if (event->events) {
LOG(FATAL) << "Unsupported epoll events: " << event->events;
}
//LOG(DEBUG) << "Epoll event " << tag("fd", event->data.fd) << tag("flags", format::as_binary(flags));
auto pollable_fd = PollableFd::from_list_node(static_cast<ListNode *>(event->data.ptr));
pollable_fd.add_flags(flags);
pollable_fd.release_as_list_node();
}
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,71 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_POLL_EPOLL
#include "td/utils/common.h"
#include "td/utils/List.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
#include "td/utils/port/PollFlags.h"
#include <sys/epoll.h>
namespace td {
namespace detail {
class Epoll final : public PollBase {
public:
Epoll() = default;
Epoll(const Epoll &) = delete;
Epoll &operator=(const Epoll &) = delete;
Epoll(Epoll &&) = delete;
Epoll &operator=(Epoll &&) = delete;
~Epoll() override = default;
void init() override;
void clear() override;
void subscribe(PollableFd fd, PollFlags flags) override;
void unsubscribe(PollableFdRef fd) override;
void unsubscribe_before_close(PollableFdRef fd) override;
void run(int timeout_ms) override;
static bool is_edge_triggered() {
return true;
}
private:
NativeFd epoll_fd_;
vector<struct epoll_event> events_;
ListNode list_root_;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/EventFdBsd.h"
char disable_linker_warning_about_empty_file_event_fd_bsd_cpp TD_UNUSED;
#ifdef TD_EVENTFD_BSD
#include "td/utils/logging.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include <fcntl.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
namespace td {
namespace detail {
// TODO: it is extremely non optimal on Darwin. kqueue events should be used instead
void EventFdBsd::init() {
int fds[2];
int err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
auto socketpair_errno = errno;
#if TD_CYGWIN
// it looks like CYGWIN bug
int max_retries = 1000000;
while (err == -1 && socketpair_errno == EADDRINUSE && max_retries-- > 0) {
err = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
socketpair_errno = errno;
}
// LOG_IF(ERROR, max_retries < 1000000) << max_retries;
#endif
LOG_IF(FATAL, err == -1) << Status::PosixError(socketpair_errno, "socketpair failed");
auto fd_a = NativeFd(fds[0]);
auto fd_b = NativeFd(fds[1]);
fd_a.set_is_blocking_unsafe(false).ensure();
fd_b.set_is_blocking_unsafe(false).ensure();
in_ = SocketFd::from_native_fd(std::move(fd_a)).move_as_ok();
out_ = SocketFd::from_native_fd(std::move(fd_b)).move_as_ok();
}
bool EventFdBsd::empty() {
return in_.empty();
}
void EventFdBsd::close() {
in_.close();
out_.close();
}
Status EventFdBsd::get_pending_error() {
return Status::OK();
}
PollableFdInfo &EventFdBsd::get_poll_info() {
return out_.get_poll_info();
}
void EventFdBsd::release() {
int value = 1;
auto result = in_.write(Slice(reinterpret_cast<const char *>(&value), sizeof(value)));
if (result.is_error()) {
LOG(FATAL) << "EventFdBsd write failed: " << result.error();
}
size_t size = result.ok();
if (size != sizeof(value)) {
LOG(FATAL) << "EventFdBsd write returned " << value << " instead of " << sizeof(value);
}
}
void EventFdBsd::acquire() {
out_.get_poll_info().add_flags(PollFlags::Read());
while (can_read(out_)) {
uint8 value[1024];
auto result = out_.read(MutableSlice(value, sizeof(value)));
if (result.is_error()) {
LOG(FATAL) << "EventFdBsd read failed:" << result.error();
}
}
}
void EventFdBsd::wait(int timeout_ms) {
pollfd fd;
fd.fd = get_poll_info().native_fd().fd();
fd.events = POLLIN;
poll(&fd, 1, timeout_ms);
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,61 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_EVENTFD_BSD
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class EventFdBsd final : public EventFdBase {
SocketFd in_;
SocketFd out_;
public:
EventFdBsd() = default;
void init() override;
bool empty() override;
void close() override;
Status get_pending_error() override TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info() override;
void release() override;
void acquire() override;
void wait(int timeout_ms) override;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,138 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/EventFdLinux.h"
char disable_linker_warning_about_empty_file_event_fd_linux_cpp TD_UNUSED;
#ifdef TD_EVENTFD_LINUX
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>
namespace td {
namespace detail {
class EventFdLinuxImpl {
public:
PollableFdInfo info;
};
EventFdLinux::EventFdLinux() = default;
EventFdLinux::EventFdLinux(EventFdLinux &&) = default;
EventFdLinux &EventFdLinux::operator=(EventFdLinux &&) = default;
EventFdLinux::~EventFdLinux() = default;
void EventFdLinux::init() {
auto fd = NativeFd(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
auto eventfd_errno = errno;
LOG_IF(FATAL, !fd) << Status::PosixError(eventfd_errno, "eventfd call failed");
impl_ = make_unique<EventFdLinuxImpl>();
impl_->info.set_native_fd(std::move(fd));
}
bool EventFdLinux::empty() {
return !impl_;
}
void EventFdLinux::close() {
impl_.reset();
}
Status EventFdLinux::get_pending_error() {
return Status::OK();
}
PollableFdInfo &EventFdLinux::get_poll_info() {
return impl_->info;
}
// NB: will be called from multiple threads
void EventFdLinux::release() {
const uint64 value = 1;
auto slice = Slice(reinterpret_cast<const char *>(&value), sizeof(value));
auto native_fd = impl_->info.native_fd().fd();
auto result = [&]() -> Result<size_t> {
auto write_res = detail::skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
auto write_errno = errno;
if (write_res >= 0) {
return narrow_cast<size_t>(write_res);
}
return Status::PosixError(write_errno, PSLICE() << "Write to fd " << native_fd << " has failed");
}();
if (result.is_error()) {
LOG(FATAL) << "EventFdLinux write failed: " << result.error();
}
size_t size = result.ok();
if (size != sizeof(value)) {
LOG(FATAL) << "EventFdLinux write returned " << value << " instead of " << sizeof(value);
}
}
void EventFdLinux::acquire() {
impl_->info.get_flags();
SCOPE_EXIT {
// Clear flags without EAGAIN and EWOULDBLOCK
// Looks like it is safe thing to do with eventfd
get_poll_info().clear_flags(PollFlags::Read());
};
uint64 res;
auto slice = MutableSlice(reinterpret_cast<char *>(&res), sizeof(res));
auto native_fd = impl_->info.native_fd().fd();
auto result = [&]() -> Result<size_t> {
CHECK(slice.size() > 0);
auto read_res = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
auto read_errno = errno;
if (read_res >= 0) {
CHECK(read_res != 0);
return narrow_cast<size_t>(read_res);
}
if (read_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
|| read_errno == EWOULDBLOCK
#endif
) {
return 0;
}
return Status::PosixError(read_errno, PSLICE() << "Read from fd " << native_fd << " has failed");
}();
if (result.is_error()) {
LOG(FATAL) << "EventFdLinux read failed: " << result.error();
}
}
void EventFdLinux::wait(int timeout_ms) {
pollfd fd;
fd.fd = get_poll_info().native_fd().fd();
fd.events = POLLIN;
poll(&fd, 1, timeout_ms);
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,63 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_EVENTFD_LINUX
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class EventFdLinuxImpl;
class EventFdLinux final : public EventFdBase {
unique_ptr<EventFdLinuxImpl> impl_;
public:
EventFdLinux();
EventFdLinux(EventFdLinux &&);
EventFdLinux &operator=(EventFdLinux &&);
~EventFdLinux();
void init() override;
bool empty() override;
void close() override;
Status get_pending_error() override TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info() override;
void release() override;
void acquire() override;
void wait(int timeout_ms) override;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,80 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/EventFdWindows.h"
char disable_linker_warning_about_empty_file_event_fd_windows_cpp TD_UNUSED;
#ifdef TD_EVENTFD_WINDOWS
#include "td/utils/logging.h"
namespace td {
namespace detail {
void EventFdWindows::init() {
auto handle = CreateEventW(nullptr, true, false, nullptr);
if (handle == nullptr) {
auto error = OS_ERROR("CreateEventW failed");
LOG(FATAL) << error;
}
event_ = NativeFd(handle);
}
bool EventFdWindows::empty() {
return !event_;
}
void EventFdWindows::close() {
event_.close();
}
Status EventFdWindows::get_pending_error() {
return Status::OK();
}
PollableFdInfo &EventFdWindows::get_poll_info() {
UNREACHABLE();
}
void EventFdWindows::release() {
if (SetEvent(event_.fd()) == 0) {
auto error = OS_ERROR("SetEvent failed");
LOG(FATAL) << error;
}
}
void EventFdWindows::acquire() {
if (ResetEvent(event_.fd()) == 0) {
auto error = OS_ERROR("ResetEvent failed");
LOG(FATAL) << error;
}
}
void EventFdWindows::wait(int timeout_ms) {
WaitForSingleObject(event_.fd(), timeout_ms);
if (ResetEvent(event_.fd()) == 0) {
auto error = OS_ERROR("ResetEvent failed");
LOG(FATAL) << error;
}
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,60 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_EVENTFD_WINDOWS
#include "td/utils/common.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class EventFdWindows final : public EventFdBase {
NativeFd event_;
public:
EventFdWindows() = default;
void init() override;
bool empty() override;
void close() override;
Status get_pending_error() override TD_WARN_UNUSED_RESULT;
PollableFdInfo &get_poll_info() override;
void release() override;
void acquire() override;
void wait(int timeout_ms) override;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,122 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/Iocp.h"
char disable_linker_warning_about_empty_file_iocp_cpp TD_UNUSED;
#ifdef TD_PORT_WINDOWS
#include "td/utils/logging.h"
namespace td {
namespace detail {
Iocp::~Iocp() {
clear();
}
void Iocp::loop() {
Iocp::Guard guard(this);
while (true) {
DWORD bytes = 0;
ULONG_PTR key = 0;
WSAOVERLAPPED *overlapped = nullptr;
BOOL ok =
GetQueuedCompletionStatus(iocp_handle_->fd(), &bytes, &key, reinterpret_cast<OVERLAPPED **>(&overlapped), 1000);
if (bytes || key || overlapped) {
// LOG(ERROR) << "Got IOCP " << bytes << " " << key << " " << overlapped;
}
if (ok) {
auto callback = reinterpret_cast<Iocp::Callback *>(key);
if (callback == nullptr) {
// LOG(ERROR) << "Interrupt IOCP loop";
return;
}
callback->on_iocp(bytes, overlapped);
} else {
if (overlapped != nullptr) {
auto error = OS_ERROR("Received from IOCP");
auto callback = reinterpret_cast<Iocp::Callback *>(key);
CHECK(callback != nullptr);
callback->on_iocp(std::move(error), overlapped);
}
}
}
}
void Iocp::interrupt_loop() {
post(0, nullptr, nullptr);
}
void Iocp::init() {
CHECK(!iocp_handle_);
auto res = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
if (res == nullptr) {
auto error = OS_ERROR("IOCP creation failed");
LOG(FATAL) << error;
}
iocp_handle_ = std::make_shared<NativeFd>(res);
}
void Iocp::clear() {
iocp_handle_.reset();
}
void Iocp::subscribe(const NativeFd &native_fd, Callback *callback) {
CHECK(iocp_handle_);
auto iocp_handle =
CreateIoCompletionPort(native_fd.fd(), iocp_handle_->fd(), reinterpret_cast<ULONG_PTR>(callback), 0);
if (iocp_handle == nullptr) {
auto error = OS_ERROR("CreateIoCompletionPort");
LOG(FATAL) << error;
}
LOG_CHECK(iocp_handle == iocp_handle_->fd()) << iocp_handle << " " << iocp_handle_->fd();
}
IocpRef Iocp::get_ref() const {
return IocpRef(iocp_handle_);
}
static void iocp_post(NativeFd &iocp_handle, size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped) {
if (PostQueuedCompletionStatus(iocp_handle.fd(), DWORD(size), reinterpret_cast<ULONG_PTR>(callback),
reinterpret_cast<OVERLAPPED *>(overlapped)) == 0) {
auto error = OS_ERROR("IOCP post failed");
LOG(FATAL) << error;
}
}
void Iocp::post(size_t size, Callback *callback, WSAOVERLAPPED *overlapped) {
iocp_post(*iocp_handle_, size, callback, overlapped);
}
IocpRef::IocpRef(std::weak_ptr<NativeFd> iocp_handle) : iocp_handle_(std::move(iocp_handle)) {
}
bool IocpRef::post(size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped) {
auto iocp_handle = iocp_handle_.lock();
if (!iocp_handle) {
return false;
}
iocp_post(*iocp_handle, size, callback, overlapped);
return true;
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,83 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_PORT_WINDOWS
#include "td/utils/common.h"
#include "td/utils/Context.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
namespace detail {
class IocpRef;
class Iocp final : public Context<Iocp> {
public:
Iocp() = default;
Iocp(const Iocp &) = delete;
Iocp &operator=(const Iocp &) = delete;
Iocp(Iocp &&) = delete;
Iocp &operator=(Iocp &&) = delete;
~Iocp();
class Callback {
public:
virtual ~Callback() = default;
virtual void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) = 0;
};
void init();
void subscribe(const NativeFd &fd, Callback *callback);
void post(size_t size, Callback *callback, WSAOVERLAPPED *overlapped);
void loop();
void interrupt_loop();
void clear();
IocpRef get_ref() const;
private:
std::shared_ptr<NativeFd> iocp_handle_;
};
class IocpRef {
public:
IocpRef() = default;
IocpRef(const Iocp &) = delete;
IocpRef &operator=(const Iocp &) = delete;
IocpRef(IocpRef &&) = default;
IocpRef &operator=(IocpRef &&) = default;
explicit IocpRef(std::weak_ptr<NativeFd> iocp_handle);
bool post(size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped);
private:
std::weak_ptr<NativeFd> iocp_handle_;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,192 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/KQueue.h"
char disable_linker_warning_about_empty_file_kqueue_cpp TD_UNUSED;
#ifdef TD_POLL_KQUEUE
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <utility>
#include <sys/time.h>
#include <unistd.h>
namespace td {
namespace detail {
KQueue::~KQueue() {
clear();
}
void KQueue::init() {
kq_ = NativeFd(kqueue());
auto kqueue_errno = errno;
LOG_IF(FATAL, !kq_) << Status::PosixError(kqueue_errno, "kqueue creation failed");
// TODO: const
events_.resize(1000);
changes_n_ = 0;
}
void KQueue::clear() {
if (!kq_) {
return;
}
events_.clear();
kq_.close();
for (auto *list_node = list_root_.next; list_node != &list_root_;) {
auto pollable_fd = PollableFd::from_list_node(list_node);
list_node = list_node->next;
}
}
int KQueue::update(int nevents, const timespec *timeout, bool may_fail) {
int err = kevent(kq_.fd(), &events_[0], changes_n_, &events_[0], nevents, timeout);
auto kevent_errno = errno;
bool is_fatal_error = [&] {
if (err != -1) {
return false;
}
if (may_fail) {
return kevent_errno != ENOENT;
}
return kevent_errno != EINTR;
}();
LOG_IF(FATAL, is_fatal_error) << Status::PosixError(kevent_errno, "kevent failed");
changes_n_ = 0;
if (err < 0) {
return 0;
}
return err;
}
void KQueue::flush_changes(bool may_fail) {
if (!changes_n_) {
return;
}
int n = update(0, nullptr, may_fail);
CHECK(n == 0);
}
void KQueue::add_change(std::uintptr_t ident, int16 filter, uint16 flags, uint32 fflags, std::intptr_t data,
void *udata) {
if (changes_n_ == static_cast<int>(events_.size())) {
flush_changes();
}
#if TD_NETBSD
auto set_udata = reinterpret_cast<std::intptr_t>(udata);
#else
auto set_udata = udata;
#endif
EV_SET(&events_[changes_n_], ident, filter, flags, fflags, data, set_udata);
VLOG(fd) << "Subscribe [fd:" << ident << "] [filter:" << filter << "] [udata: " << udata << "]";
changes_n_++;
}
void KQueue::subscribe(PollableFd fd, PollFlags flags) {
auto native_fd = fd.native_fd().fd();
auto list_node = fd.release_as_list_node();
list_root_.put(list_node);
if (flags.can_read()) {
add_change(native_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, list_node);
}
if (flags.can_write()) {
add_change(native_fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, list_node);
}
}
void KQueue::invalidate(int native_fd) {
for (int i = 0; i < changes_n_; i++) {
if (events_[i].ident == static_cast<std::uintptr_t>(native_fd)) {
changes_n_--;
std::swap(events_[i], events_[changes_n_]);
i--;
}
}
}
void KQueue::unsubscribe(PollableFdRef fd_ref) {
auto pollable_fd = fd_ref.lock();
auto native_fd = pollable_fd.native_fd().fd();
// invalidate(fd);
flush_changes();
add_change(native_fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
flush_changes(true);
add_change(native_fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);
flush_changes(true);
}
void KQueue::unsubscribe_before_close(PollableFdRef fd_ref) {
auto pollable_fd = fd_ref.lock();
invalidate(pollable_fd.native_fd().fd());
// just to avoid O(changes_n ^ 2)
if (changes_n_ != 0) {
flush_changes();
}
}
void KQueue::run(int timeout_ms) {
timespec timeout_data;
timespec *timeout_ptr;
if (timeout_ms == -1) {
timeout_ptr = nullptr;
} else {
timeout_data.tv_sec = timeout_ms / 1000;
timeout_data.tv_nsec = timeout_ms % 1000 * 1000000;
timeout_ptr = &timeout_data;
}
int n = update(static_cast<int>(events_.size()), timeout_ptr);
for (int i = 0; i < n; i++) {
struct kevent *event = &events_[i];
PollFlags flags;
if (event->filter == EVFILT_WRITE) {
flags.add_flags(PollFlags::Write());
}
if (event->filter == EVFILT_READ) {
flags.add_flags(PollFlags::Read());
}
if (event->flags & EV_EOF) {
flags.add_flags(PollFlags::Close());
}
if (event->fflags & EV_ERROR) {
LOG(FATAL) << "EV_ERROR in kqueue is not supported";
}
#if TD_NETBSD
auto udata = reinterpret_cast<void *>(event->udata);
#else
auto udata = event->udata;
#endif
VLOG(fd) << "Event [fd:" << event->ident << "] [filter:" << event->filter << "] [udata: " << udata << "]";
// LOG(WARNING) << "Have event->ident = " << event->ident << "event->filter = " << event->filter;
auto pollable_fd = PollableFd::from_list_node(static_cast<ListNode *>(udata));
pollable_fd.add_flags(flags);
pollable_fd.release_as_list_node();
}
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,84 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_POLL_KQUEUE
#include "td/utils/common.h"
#include "td/utils/List.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
#include "td/utils/port/PollFlags.h"
#include <cstdint>
#include <sys/types.h> // must be included before sys/event.h, which depends on sys/types.h on FreeBSD
#include <sys/event.h>
namespace td {
namespace detail {
class KQueue final : public PollBase {
public:
KQueue() = default;
KQueue(const KQueue &) = delete;
KQueue &operator=(const KQueue &) = delete;
KQueue(KQueue &&) = delete;
KQueue &operator=(KQueue &&) = delete;
~KQueue() override;
void init() override;
void clear() override;
void subscribe(PollableFd fd, PollFlags flags) override;
void unsubscribe(PollableFdRef fd) override;
void unsubscribe_before_close(PollableFdRef fd) override;
void run(int timeout_ms) override;
static bool is_edge_triggered() {
return true;
}
private:
vector<struct kevent> events_;
int changes_n_;
NativeFd kq_;
ListNode list_root_;
int update(int nevents, const timespec *timeout, bool may_fail = false);
void invalidate(int native_fd);
void flush_changes(bool may_fail = false);
void add_change(std::uintptr_t ident, int16 filter, uint16 flags, uint32 fflags, std::intptr_t data, void *udata);
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,259 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#if TD_PORT_POSIX
#include <fcntl.h>
#include <unistd.h>
#endif
#if TD_FD_DEBUG
#include <mutex>
#include <set>
#endif
namespace td {
#if TD_FD_DEBUG
class FdSet {
public:
void on_create_fd(NativeFd::Fd fd) {
CHECK(is_valid(fd));
if (is_stdio(fd)) {
return;
}
std::unique_lock<std::mutex> guard(mutex_);
if (fds_.count(fd) >= 1) {
LOG(FATAL) << "Create duplicated fd: " << fd;
}
fds_.insert(fd);
}
Status validate(NativeFd::Fd fd) {
if (!is_valid(fd)) {
return Status::Error(PSLICE() << "Invalid fd: " << fd);
}
if (is_stdio(fd)) {
return Status::OK();
}
std::unique_lock<std::mutex> guard(mutex_);
if (fds_.count(fd) != 1) {
return Status::Error(PSLICE() << "Unknown fd: " << fd);
}
return Status::OK();
}
void on_close_fd(NativeFd::Fd fd) {
CHECK(is_valid(fd));
if (is_stdio(fd)) {
return;
}
std::unique_lock<std::mutex> guard(mutex_);
if (fds_.count(fd) != 1) {
LOG(FATAL) << "Close unknown fd: " << fd;
}
fds_.erase(fd);
}
private:
std::mutex mutex_;
std::set<NativeFd::Fd> fds_;
bool is_stdio(NativeFd::Fd fd) const {
#if TD_PORT_WINDOWS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
return fd == GetStdHandle(STD_INPUT_HANDLE) || fd == GetStdHandle(STD_OUTPUT_HANDLE) ||
fd == GetStdHandle(STD_ERROR_HANDLE);
#else
return false;
#endif
#else
return fd >= 0 && fd <= 2;
#endif
}
bool is_valid(NativeFd::Fd fd) const {
#if TD_PORT_WINDOWS
return fd != INVALID_HANDLE_VALUE;
#else
return fd >= 0;
#endif
}
};
namespace {
FdSet &get_fd_set() {
static FdSet res;
return res;
}
} // namespace
#endif
Status NativeFd::validate() const {
#if TD_FD_DEBUG
return get_fd_set().validate(fd_.get());
#else
return Status::OK();
#endif
}
NativeFd::NativeFd(Fd fd) : fd_(fd) {
VLOG(fd) << *this << " create";
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
#endif
}
NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
#endif
}
#if TD_PORT_WINDOWS
NativeFd::NativeFd(Socket socket) : fd_(reinterpret_cast<Fd>(socket)), is_socket_(true) {
VLOG(fd) << *this << " create";
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
#endif
}
#endif
NativeFd &NativeFd::operator=(NativeFd &&from) {
CHECK(this != &from);
close();
fd_ = std::move(from.fd_);
#if TD_PORT_WINDOWS
is_socket_ = from.is_socket_;
#endif
return *this;
}
NativeFd::~NativeFd() {
close();
}
NativeFd::operator bool() const {
return fd_.get() != empty_fd();
}
NativeFd::Fd NativeFd::empty_fd() {
#if TD_PORT_POSIX
return -1;
#elif TD_PORT_WINDOWS
return INVALID_HANDLE_VALUE;
#endif
}
NativeFd::Fd NativeFd::fd() const {
return fd_.get();
}
NativeFd::Socket NativeFd::socket() const {
#if TD_PORT_POSIX
return fd();
#elif TD_PORT_WINDOWS
CHECK(is_socket_);
return reinterpret_cast<Socket>(fd_.get());
#endif
}
Status NativeFd::set_is_blocking(bool is_blocking) const {
#if TD_PORT_POSIX
auto old_flags = fcntl(fd(), F_GETFL);
if (old_flags == -1) {
return OS_SOCKET_ERROR("Failed to get socket flags");
}
auto new_flags = is_blocking ? old_flags & ~O_NONBLOCK : old_flags | O_NONBLOCK;
if (new_flags != old_flags && fcntl(fd(), F_SETFL, new_flags) == -1) {
return OS_SOCKET_ERROR("Failed to set socket flags");
}
return Status::OK();
#elif TD_PORT_WINDOWS
return set_is_blocking_unsafe(is_blocking);
#endif
}
Status NativeFd::set_is_blocking_unsafe(bool is_blocking) const {
#if TD_PORT_POSIX
if (fcntl(fd(), F_SETFL, is_blocking ? 0 : O_NONBLOCK) == -1) {
#elif TD_PORT_WINDOWS
u_long mode = is_blocking;
if (ioctlsocket(socket(), FIONBIO, &mode) != 0) {
#endif
return OS_SOCKET_ERROR("Failed to change socket flags");
}
return Status::OK();
}
Status NativeFd::duplicate(const NativeFd &to) const {
#if TD_PORT_POSIX
CHECK(*this);
CHECK(to);
if (dup2(fd(), to.fd()) == -1) {
return OS_ERROR("Failed to duplicate file descriptor");
}
return Status::OK();
#elif TD_PORT_WINDOWS
return Status::Error("Not supported");
#endif
}
void NativeFd::close() {
if (!*this) {
return;
}
#if TD_FD_DEBUG
get_fd_set().on_close_fd(fd());
#endif
VLOG(fd) << *this << " close";
#if TD_PORT_WINDOWS
if (is_socket_ ? closesocket(socket()) : !CloseHandle(fd())) {
#elif TD_PORT_POSIX
if (::close(fd()) < 0) {
#endif
auto error = OS_ERROR("Close fd");
LOG(ERROR) << error;
}
fd_ = {};
}
NativeFd::Fd NativeFd::release() {
VLOG(fd) << *this << " release";
auto res = fd_.get();
fd_ = {};
#if TD_FD_DEBUG
get_fd_set().on_close_fd(res);
#endif
return res;
}
StringBuilder &operator<<(StringBuilder &sb, const NativeFd &fd) {
return sb << tag("fd", fd.fd());
}
} // namespace td

View file

@ -0,0 +1,80 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
namespace td {
class NativeFd {
public:
#if TD_PORT_POSIX
using Fd = int;
using Socket = int;
#elif TD_PORT_WINDOWS
using Fd = HANDLE;
using Socket = SOCKET;
#endif
NativeFd() = default;
NativeFd(NativeFd &&) = default;
NativeFd &operator=(NativeFd &&);
explicit NativeFd(Fd fd);
NativeFd(Fd fd, bool nolog);
#if TD_PORT_WINDOWS
explicit NativeFd(Socket socket);
#endif
NativeFd(const NativeFd &) = delete;
NativeFd &operator=(const NativeFd &) = delete;
~NativeFd();
explicit operator bool() const;
static Fd empty_fd();
Fd fd() const;
Socket socket() const;
Status set_is_blocking(bool is_blocking) const;
Status set_is_blocking_unsafe(bool is_blocking) const; // may drop other Fd flags on non-Windows
Status duplicate(const NativeFd &to) const;
void close();
Fd release();
Status validate() const;
private:
#if TD_PORT_POSIX
MovableValue<Fd, -1> fd_;
#elif TD_PORT_WINDOWS
MovableValue<Fd, INVALID_HANDLE_VALUE> fd_;
bool is_socket_{false};
#endif
};
StringBuilder &operator<<(StringBuilder &sb, const NativeFd &fd);
} // namespace td

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/Poll.h"
char disable_linker_warning_about_empty_file_poll_cpp TD_UNUSED;
#ifdef TD_POLL_POLL
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
void Poll::init() {
}
void Poll::clear() {
pollfds_.clear();
}
void Poll::subscribe(PollableFd fd, PollFlags flags) {
unsubscribe(fd.ref());
struct pollfd pollfd;
pollfd.fd = fd.native_fd().fd();
pollfd.events = 0;
if (flags.can_read()) {
pollfd.events |= POLLIN;
}
if (flags.can_write()) {
pollfd.events |= POLLOUT;
}
pollfd.revents = 0;
pollfds_.push_back(pollfd);
fds_.push_back(std::move(fd));
}
void Poll::unsubscribe(PollableFdRef fd_ref) {
auto fd = fd_ref.lock();
SCOPE_EXIT {
fd.release_as_list_node();
};
for (auto it = pollfds_.begin(); it != pollfds_.end(); ++it) {
if (it->fd == fd.native_fd().fd()) {
pollfds_.erase(it);
fds_.erase(fds_.begin() + (it - pollfds_.begin()));
return;
}
}
}
void Poll::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
void Poll::run(int timeout_ms) {
int err = poll(pollfds_.data(), narrow_cast<int>(pollfds_.size()), timeout_ms);
auto poll_errno = errno;
LOG_IF(FATAL, err == -1 && poll_errno != EINTR) << Status::PosixError(poll_errno, "poll failed");
for (size_t i = 0; i < pollfds_.size(); i++) {
auto &pollfd = pollfds_[i];
auto &fd = fds_[i];
PollFlags flags;
if (pollfd.revents & POLLIN) {
pollfd.revents &= ~POLLIN;
flags = flags | PollFlags::Read();
}
if (pollfd.revents & POLLOUT) {
pollfd.revents &= ~POLLOUT;
flags = flags | PollFlags::Write();
}
if (pollfd.revents & POLLHUP) {
pollfd.revents &= ~POLLHUP;
flags = flags | PollFlags::Close();
}
if (pollfd.revents & POLLERR) {
pollfd.revents &= ~POLLERR;
flags = flags | PollFlags::Error();
}
if (pollfd.revents & POLLNVAL) {
LOG(FATAL) << "Unexpected POLLNVAL " << tag("fd", pollfd.fd);
}
if (pollfd.revents) {
LOG(FATAL) << "Unsupported poll events: " << pollfd.revents;
}
fd.add_flags(flags);
}
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,68 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_POLL_POLL
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
#include "td/utils/port/PollFlags.h"
#include <poll.h>
namespace td {
namespace detail {
class Poll final : public PollBase {
public:
Poll() = default;
Poll(const Poll &) = delete;
Poll &operator=(const Poll &) = delete;
Poll(Poll &&) = delete;
Poll &operator=(Poll &&) = delete;
~Poll() override = default;
void init() override;
void clear() override;
void subscribe(PollableFd fd, PollFlags flags) override;
void unsubscribe(PollableFdRef fd) override;
void unsubscribe_before_close(PollableFdRef fd) override;
void run(int timeout_ms) override;
static bool is_edge_triggered() {
return false;
}
private:
vector<pollfd> pollfds_;
vector<PollableFd> fds_;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,262 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/List.h"
#include "td/utils/logging.h"
#include "td/utils/Observer.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/SpinLock.h"
#include <atomic>
#include <memory>
#include <type_traits>
namespace td {
class PollableFdInfo;
class PollableFdInfoUnlock {
public:
void operator()(PollableFdInfo *ptr);
};
class PollableFd;
class PollableFdRef {
public:
explicit PollableFdRef(ListNode *list_node) : list_node_(list_node) {
}
PollableFd lock();
private:
ListNode *list_node_;
};
class PollableFd {
public:
// Interface for kqueue, epoll and e.t.c.
const NativeFd &native_fd() const;
ListNode *release_as_list_node();
PollableFdRef ref();
static PollableFd from_list_node(ListNode *node);
void add_flags(PollFlags flags);
PollFlags get_flags_unsafe() const;
private:
std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info_;
friend class PollableFdInfo;
explicit PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info) : fd_info_(std::move(fd_info)) {
}
};
inline PollableFd PollableFdRef::lock() {
return PollableFd::from_list_node(list_node_);
}
class PollableFdInfo : private ListNode {
public:
PollableFdInfo() = default;
PollableFdInfo(const PollableFdInfo &) = delete;
PollableFdInfo &operator=(const PollableFdInfo &) = delete;
PollableFdInfo(PollableFdInfo &&) = delete;
PollableFdInfo &operator=(PollableFdInfo &&) = delete;
PollableFd extract_pollable_fd(ObserverBase *observer) {
VLOG(fd) << native_fd() << " extract pollable fd " << tag("observer", observer);
CHECK(!empty());
bool was_locked = lock_.test_and_set(std::memory_order_acquire);
CHECK(!was_locked);
set_observer(observer);
return PollableFd{std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>{this}};
}
PollableFdRef get_pollable_fd_ref() {
CHECK(!empty());
bool was_locked = lock_.test_and_set(std::memory_order_acquire);
CHECK(was_locked);
return PollableFdRef{as_list_node()};
}
void add_flags(PollFlags flags) {
flags_.write_flags_local(flags);
}
void clear_flags(PollFlags flags) {
flags_.clear_flags(flags);
}
PollFlags get_flags() const {
return flags_.read_flags();
}
PollFlags get_flags_local() const {
return flags_.read_flags_local();
}
bool empty() const {
return !fd_;
}
void set_native_fd(NativeFd new_native_fd) {
if (fd_) {
CHECK(!new_native_fd);
bool was_locked = lock_.test_and_set(std::memory_order_acquire);
CHECK(!was_locked);
lock_.clear(std::memory_order_release);
}
fd_ = std::move(new_native_fd);
}
explicit PollableFdInfo(NativeFd native_fd) {
set_native_fd(std::move(native_fd));
}
const NativeFd &native_fd() const {
//CHECK(!empty());
return fd_;
}
NativeFd move_as_native_fd() {
return std::move(fd_);
}
~PollableFdInfo() {
VLOG(fd) << native_fd() << " destroy PollableFdInfo";
bool was_locked = lock_.test_and_set(std::memory_order_acquire);
CHECK(!was_locked);
}
void add_flags_from_poll(PollFlags flags) {
VLOG(fd) << native_fd() << " add flags from poll " << flags;
if (flags_.write_flags(flags)) {
notify_observer();
}
}
private:
NativeFd fd_{};
std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
PollFlagsSet flags_;
#if TD_PORT_WINDOWS
SpinLock observer_lock_;
#endif
ObserverBase *observer_{nullptr};
friend class PollableFd;
friend class PollableFdInfoUnlock;
void set_observer(ObserverBase *observer) {
#if TD_PORT_WINDOWS
auto lock = observer_lock_.lock();
#endif
CHECK(!observer_);
observer_ = observer;
}
void clear_observer() {
#if TD_PORT_WINDOWS
auto lock = observer_lock_.lock();
#endif
observer_ = nullptr;
}
void notify_observer() {
#if TD_PORT_WINDOWS
auto lock = observer_lock_.lock();
#endif
VLOG(fd) << native_fd() << " notify " << tag("observer", observer_);
if (observer_) {
observer_->notify();
}
}
void unlock() {
clear_observer();
lock_.clear(std::memory_order_release);
as_list_node()->remove();
}
ListNode *as_list_node() {
return static_cast<ListNode *>(this);
}
static PollableFdInfo *from_list_node(ListNode *list_node) {
return static_cast<PollableFdInfo *>(list_node);
}
};
inline void PollableFdInfoUnlock::operator()(PollableFdInfo *ptr) {
ptr->unlock();
}
inline ListNode *PollableFd::release_as_list_node() {
return fd_info_.release()->as_list_node();
}
inline PollableFdRef PollableFd::ref() {
return PollableFdRef{fd_info_->as_list_node()};
}
inline PollableFd PollableFd::from_list_node(ListNode *node) {
return PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>(PollableFdInfo::from_list_node(node)));
}
inline void PollableFd::add_flags(PollFlags flags) {
fd_info_->add_flags_from_poll(flags);
}
inline PollFlags PollableFd::get_flags_unsafe() const {
return fd_info_->get_flags_local();
}
inline const NativeFd &PollableFd::native_fd() const {
return fd_info_->native_fd();
}
#if TD_PORT_POSIX
namespace detail {
template <class F>
auto skip_eintr(F &&f) {
decltype(f()) res;
static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
do {
errno = 0; // just in case
res = f();
} while (res < 0 && errno == EINTR);
return res;
}
template <class F>
auto skip_eintr_cstr(F &&f) {
char *res;
do {
errno = 0; // just in case
res = f();
} while (res == nullptr && errno == EINTR);
return res;
}
} // namespace detail
#endif
template <class FdT>
bool can_read(const FdT &fd) {
return fd.get_poll_info().get_flags().can_read() || fd.get_poll_info().get_flags().has_pending_error();
}
template <class FdT>
bool can_write(const FdT &fd) {
return fd.get_poll_info().get_flags().can_write();
}
template <class FdT>
bool can_close(const FdT &fd) {
return fd.get_poll_info().get_flags().can_close();
}
} // namespace td

View file

@ -0,0 +1,132 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/Select.h"
char disable_linker_warning_about_empty_file_select_cpp TD_UNUSED;
#ifdef TD_POLL_SELECT
#include "td/utils/logging.h"
#include <utility>
namespace td {
namespace detail {
void Select::init() {
FD_ZERO(&all_fd_);
FD_ZERO(&read_fd_);
FD_ZERO(&write_fd_);
FD_ZERO(&except_fd_);
max_fd_ = -1;
}
void Select::clear() {
fds_.clear();
}
void Select::subscribe(PollableFd fd, PollFlags flags) {
int native_fd = fd.native_fd().fd();
for (auto &it : fds_) {
CHECK(it.fd.native_fd().fd() != native_fd);
}
fds_.push_back(FdInfo{std::move(fd), flags});
LOG_CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
FD_SET(native_fd, &all_fd_);
if (native_fd > max_fd_) {
max_fd_ = native_fd;
}
}
void Select::unsubscribe(PollableFdRef fd) {
auto fd_locked = fd.lock();
int native_fd = fd_locked.native_fd().fd();
fd_locked.release_as_list_node();
LOG_CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
FD_CLR(native_fd, &all_fd_);
FD_CLR(native_fd, &read_fd_);
FD_CLR(native_fd, &write_fd_);
FD_CLR(native_fd, &except_fd_);
while (max_fd_ >= 0 && !FD_ISSET(max_fd_, &all_fd_)) {
max_fd_--;
}
for (auto it = fds_.begin(); it != fds_.end();) {
if (it->fd.native_fd().fd() == native_fd) {
std::swap(*it, fds_.back());
fds_.pop_back();
break;
} else {
++it;
}
}
}
void Select::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
void Select::run(int timeout_ms) {
timeval timeout_data;
timeval *timeout_ptr;
if (timeout_ms == -1) {
timeout_ptr = nullptr;
} else {
timeout_data.tv_sec = timeout_ms / 1000;
timeout_data.tv_usec = timeout_ms % 1000 * 1000;
timeout_ptr = &timeout_data;
}
for (auto &it : fds_) {
int native_fd = it.fd.native_fd().fd();
PollFlags fd_flags = it.fd.get_flags_unsafe(); // concurrent calls are UB
if (it.flags.can_write() && !fd_flags.can_write()) {
FD_SET(native_fd, &write_fd_);
} else {
FD_CLR(native_fd, &write_fd_);
}
if (it.flags.can_read() && !fd_flags.can_read()) {
FD_SET(native_fd, &read_fd_);
} else {
FD_CLR(native_fd, &read_fd_);
}
FD_SET(native_fd, &except_fd_);
}
select(max_fd_ + 1, &read_fd_, &write_fd_, &except_fd_, timeout_ptr);
for (auto &it : fds_) {
int native_fd = it.fd.native_fd().fd();
PollFlags flags;
if (FD_ISSET(native_fd, &read_fd_)) {
flags = flags | PollFlags::Read();
}
if (FD_ISSET(native_fd, &write_fd_)) {
flags = flags | PollFlags::Write();
}
if (FD_ISSET(native_fd, &except_fd_)) {
flags = flags | PollFlags::Error();
}
it.fd.add_flags(flags);
}
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,76 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_POLL_SELECT
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
#include "td/utils/port/PollFlags.h"
#include <sys/select.h>
namespace td {
namespace detail {
class Select final : public PollBase {
public:
Select() = default;
Select(const Select &) = delete;
Select &operator=(const Select &) = delete;
Select(Select &&) = delete;
Select &operator=(Select &&) = delete;
~Select() override = default;
void init() override;
void clear() override;
void subscribe(PollableFd fd, PollFlags flags) override;
void unsubscribe(PollableFdRef fd) override;
void unsubscribe_before_close(PollableFdRef fd) override;
void run(int timeout_ms) override;
static bool is_edge_triggered() {
return false;
}
private:
struct FdInfo {
PollableFd fd;
PollFlags flags;
};
vector<FdInfo> fds_;
fd_set all_fd_;
fd_set read_fd_;
fd_set write_fd_;
fd_set except_fd_;
int max_fd_;
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,64 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/common.h"
#include "td/utils/port/thread_local.h"
#include <mutex>
#include <set>
namespace td {
namespace detail {
class ThreadIdManager {
public:
int32 register_thread() {
std::lock_guard<std::mutex> guard(mutex_);
if (unused_thread_ids_.empty()) {
return ++max_thread_id_;
}
auto it = unused_thread_ids_.begin();
auto result = *it;
unused_thread_ids_.erase(it);
return result;
}
void unregister_thread(int32 thread_id) {
std::lock_guard<std::mutex> guard(mutex_);
CHECK(0 < thread_id && thread_id <= max_thread_id_);
bool is_inserted = unused_thread_ids_.insert(thread_id).second;
CHECK(is_inserted);
}
private:
std::mutex mutex_;
std::set<int32> unused_thread_ids_;
int32 max_thread_id_ = 0;
};
static ThreadIdManager thread_id_manager;
ThreadIdGuard::ThreadIdGuard() {
thread_id_ = thread_id_manager.register_thread();
set_thread_id(thread_id_);
}
ThreadIdGuard::~ThreadIdGuard() {
thread_id_manager.unregister_thread(thread_id_);
set_thread_id(0);
}
} // namespace detail
} // namespace td

View file

@ -0,0 +1,38 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
namespace td {
namespace detail {
class ThreadIdGuard {
public:
ThreadIdGuard();
~ThreadIdGuard();
ThreadIdGuard(const ThreadIdGuard &) = delete;
ThreadIdGuard &operator=(const ThreadIdGuard &) = delete;
ThreadIdGuard(ThreadIdGuard &&) = delete;
ThreadIdGuard &operator=(ThreadIdGuard &&) = delete;
private:
int32 thread_id_;
};
} // namespace detail
} // namespace td

View file

@ -0,0 +1,121 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_THREAD_PTHREAD
#include "td/utils/common.h"
#include "td/utils/Destructor.h"
#include "td/utils/invoke.h"
#include "td/utils/MovableValue.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
#include <tuple>
#include <type_traits>
#include <utility>
#include <pthread.h>
#include <sched.h>
namespace td {
namespace detail {
class ThreadPthread {
public:
ThreadPthread() = default;
ThreadPthread(const ThreadPthread &other) = delete;
ThreadPthread &operator=(const ThreadPthread &other) = delete;
ThreadPthread(ThreadPthread &&) = default;
ThreadPthread &operator=(ThreadPthread &&other) {
join();
is_inited_ = std::move(other.is_inited_);
thread_ = other.thread_;
return *this;
}
template <class Function, class... Args>
explicit ThreadPthread(Function &&f, Args &&... args) {
auto func = create_destructor([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...)]() mutable {
invoke_tuple(std::move(args));
clear_thread_locals();
});
pthread_create(&thread_, nullptr, run_thread, func.release());
is_inited_ = true;
}
void set_name(CSlice name) {
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ(2, 12)
pthread_setname_np(thread_, name.c_str());
#endif
#endif
}
void join() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_join(thread_, nullptr);
}
}
void detach() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_detach(thread_);
}
}
~ThreadPthread() {
join();
}
static unsigned hardware_concurrency() {
return 8;
}
using id = pthread_t;
private:
MovableValue<bool> is_inited_;
pthread_t thread_;
template <class T>
std::decay_t<T> decay_copy(T &&v) {
return std::forward<T>(v);
}
static void *run_thread(void *ptr) {
ThreadIdGuard thread_id_guard;
auto func = unique_ptr<Destructor>(static_cast<Destructor *>(ptr));
return nullptr;
}
};
namespace this_thread_pthread {
inline void yield() {
sched_yield();
}
inline ThreadPthread::id get_id() {
return pthread_self();
}
} // namespace this_thread_pthread
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,89 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_THREAD_STL
#include "td/utils/common.h"
#include "td/utils/invoke.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
namespace td {
namespace detail {
class ThreadStl {
public:
ThreadStl() = default;
ThreadStl(const ThreadStl &other) = delete;
ThreadStl &operator=(const ThreadStl &other) = delete;
ThreadStl(ThreadStl &&) = default;
ThreadStl &operator=(ThreadStl &&) = default;
~ThreadStl() {
join();
}
template <class Function, class... Args>
explicit ThreadStl(Function &&f, Args &&... args) {
thread_ = std::thread([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...)]() mutable {
ThreadIdGuard thread_id_guard;
invoke_tuple(std::move(args));
clear_thread_locals();
});
}
void join() {
if (thread_.joinable()) {
thread_.join();
}
}
void detach() {
if (thread_.joinable()) {
thread_.detach();
}
}
void set_name(CSlice name) {
}
static unsigned hardware_concurrency() {
return std::thread::hardware_concurrency();
}
using id = std::thread::id;
private:
std::thread thread_;
template <class T>
std::decay_t<T> decay_copy(T &&v) {
return std::forward<T>(v);
}
};
namespace this_thread_stl = std::this_thread;
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,55 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/detail/WineventPoll.h"
char disable_linker_warning_about_empty_file_wineventpoll_cpp TD_UNUSED;
#ifdef TD_POLL_WINEVENT
#include "td/utils/common.h"
namespace td {
namespace detail {
void WineventPoll::init() {
}
void WineventPoll::clear() {
}
void WineventPoll::subscribe(PollableFd fd, PollFlags flags) {
fd.release_as_list_node();
}
void WineventPoll::unsubscribe(PollableFdRef fd) {
auto pollable_fd = fd.lock(); // unlocked in destructor
}
void WineventPoll::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(std::move(fd));
}
void WineventPoll::run(int timeout_ms) {
UNREACHABLE();
}
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,62 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#ifdef TD_POLL_WINEVENT
#include "td/utils/common.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
#include "td/utils/port/PollFlags.h"
namespace td {
namespace detail {
class WineventPoll final : public PollBase {
public:
WineventPoll() = default;
WineventPoll(const WineventPoll &) = delete;
WineventPoll &operator=(const WineventPoll &) = delete;
WineventPoll(WineventPoll &&) = delete;
WineventPoll &operator=(WineventPoll &&) = delete;
~WineventPoll() override = default;
void init() override;
void clear() override;
void subscribe(PollableFd fd, PollFlags flags) override;
void unsubscribe(PollableFdRef fd) override;
void unsubscribe_before_close(PollableFdRef fd) override;
void run(int timeout_ms) override;
static bool is_edge_triggered() {
return true;
}
};
} // namespace detail
} // namespace td
#endif

View file

@ -0,0 +1,635 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/path.h"
#include "td/utils/port/config.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/ScopeGuard.h"
#if TD_PORT_WINDOWS
#include "td/utils/port/wstring_convert.h"
#include "td/utils/Random.h"
#endif
#if TD_PORT_POSIX
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
// We don't want warnings from system headers
#if TD_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
#include <sys/stat.h>
#if TD_GCC
#pragma GCC diagnostic pop
#endif
#include <sys/types.h>
#include <unistd.h>
#endif
#if TD_DARWIN
#include <sys/syslimits.h>
#endif
#include <cstdlib>
#include <string>
namespace td {
static string temporary_dir;
Status set_temporary_dir(CSlice dir) {
string input_dir = dir.str();
if (!dir.empty() && dir.back() != TD_DIR_SLASH) {
input_dir += TD_DIR_SLASH;
}
TRY_STATUS(mkpath(input_dir, 0750));
TRY_RESULT(real_dir, realpath(input_dir));
temporary_dir = std::move(real_dir);
return Status::OK();
}
Status mkpath(CSlice path, int32 mode) {
Status first_error = Status::OK();
Status last_error = Status::OK();
for (size_t i = 1; i < path.size(); i++) {
if (path[i] == TD_DIR_SLASH) {
last_error = mkdir(PSLICE() << path.substr(0, i), mode);
if (last_error.is_error() && first_error.is_ok()) {
first_error = last_error.clone();
}
}
}
if (last_error.is_error()) {
return first_error;
}
return Status::OK();
}
Status rmrf(CSlice path) {
return walk_path(path, [](CSlice path, WalkPath::Type type) {
switch (type) {
case WalkPath::Type::EnterDir:
break;
case WalkPath::Type::ExitDir:
rmdir(path).ignore();
break;
case WalkPath::Type::NotDir:
unlink(path).ignore();
break;
}
});
}
#if TD_PORT_POSIX
Status mkdir(CSlice dir, int32 mode) {
int mkdir_res = [&] {
int res;
do {
errno = 0; // just in case
res = ::mkdir(dir.c_str(), static_cast<mode_t>(mode));
} while (res < 0 && (errno == EINTR || errno == EAGAIN));
return res;
}();
if (mkdir_res == 0) {
return Status::OK();
}
auto mkdir_errno = errno;
if (mkdir_errno == EEXIST) {
// TODO check that it is a directory
return Status::OK();
}
return Status::PosixError(mkdir_errno, PSLICE() << "Can't create directory \"" << dir << '"');
}
Status rename(CSlice from, CSlice to) {
int rename_res = detail::skip_eintr([&] { return ::rename(from.c_str(), to.c_str()); });
if (rename_res < 0) {
return OS_ERROR(PSLICE() << "Can't rename \"" << from << "\" to \"" << to << '\"');
}
return Status::OK();
}
Result<string> realpath(CSlice slice, bool ignore_access_denied) {
char full_path[PATH_MAX + 1];
string res;
char *err = detail::skip_eintr_cstr([&] { return ::realpath(slice.c_str(), full_path); });
if (err != full_path) {
if (ignore_access_denied && (errno == EACCES || errno == EPERM)) {
res = slice.str();
} else {
return OS_ERROR(PSLICE() << "Realpath failed for \"" << slice << '"');
}
} else {
res = full_path;
}
if (res.empty()) {
return Status::Error("Empty path");
}
if (!slice.empty() && slice.end()[-1] == TD_DIR_SLASH) {
if (res.back() != TD_DIR_SLASH) {
res += TD_DIR_SLASH;
}
}
return res;
}
Status chdir(CSlice dir) {
int chdir_res = detail::skip_eintr([&] { return ::chdir(dir.c_str()); });
if (chdir_res) {
return OS_ERROR(PSLICE() << "Can't change directory to \"" << dir << '"');
}
return Status::OK();
}
Status rmdir(CSlice dir) {
int rmdir_res = detail::skip_eintr([&] { return ::rmdir(dir.c_str()); });
if (rmdir_res) {
return OS_ERROR(PSLICE() << "Can't delete directory \"" << dir << '"');
}
return Status::OK();
}
Status unlink(CSlice path) {
int unlink_res = detail::skip_eintr([&] { return ::unlink(path.c_str()); });
if (unlink_res) {
return OS_ERROR(PSLICE() << "Can't unlink \"" << path << '"');
}
return Status::OK();
}
CSlice get_temporary_dir() {
static bool is_inited = [] {
if (temporary_dir.empty()) {
const char *s = std::getenv("TMPDIR");
if (s != nullptr && s[0] != '\0') {
temporary_dir = s;
} else if (P_tmpdir != nullptr && P_tmpdir[0] != '\0') {
temporary_dir = P_tmpdir;
} else {
return false;
}
}
if (temporary_dir.size() > 1 && temporary_dir.back() == TD_DIR_SLASH) {
temporary_dir.pop_back();
}
return true;
}();
LOG_IF(FATAL, !is_inited) << "Can't find temporary directory";
return temporary_dir;
}
Result<std::pair<FileFd, string>> mkstemp(CSlice dir) {
if (dir.empty()) {
dir = get_temporary_dir();
if (dir.empty()) {
return Status::Error("Can't find temporary directory");
}
}
TRY_RESULT(dir_real, realpath(dir));
CHECK(!dir_real.empty());
string file_pattern;
file_pattern.reserve(dir_real.size() + 14);
file_pattern = dir_real;
if (file_pattern.back() != TD_DIR_SLASH) {
file_pattern += TD_DIR_SLASH;
}
file_pattern += "tmpXXXXXXXXXX";
int fd = detail::skip_eintr([&] { return ::mkstemp(&file_pattern[0]); });
if (fd == -1) {
return OS_ERROR(PSLICE() << "Can't create temporary file \"" << file_pattern << '"');
}
if (close(fd)) {
return OS_ERROR(PSLICE() << "Can't close temporary file \"" << file_pattern << '"');
}
// TODO create file from fd
TRY_RESULT(file, FileFd::open(file_pattern, FileFd::Write | FileFd::Truncate | FileFd::Append));
return std::make_pair(std::move(file), std::move(file_pattern));
}
Result<string> mkdtemp(CSlice dir, Slice prefix) {
if (dir.empty()) {
dir = get_temporary_dir();
if (dir.empty()) {
return Status::Error("Can't find temporary directory");
}
}
TRY_RESULT(dir_real, realpath(dir));
CHECK(!dir_real.empty());
string dir_pattern;
dir_pattern.reserve(dir_real.size() + prefix.size() + 7);
dir_pattern = dir_real;
if (dir_pattern.back() != TD_DIR_SLASH) {
dir_pattern += TD_DIR_SLASH;
}
dir_pattern.append(prefix.begin(), prefix.size());
dir_pattern += "XXXXXX";
char *result = detail::skip_eintr_cstr([&] { return ::mkdtemp(&dir_pattern[0]); });
if (result == nullptr) {
return OS_ERROR(PSLICE() << "Can't create temporary directory \"" << dir_pattern << '"');
}
return result;
}
namespace detail {
using WalkFunction = std::function<WalkPath::Action(CSlice name, WalkPath::Type type)>;
Result<bool> walk_path_dir(string &path, FileFd fd, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
Result<bool> walk_path_dir(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
Result<bool> walk_path_file(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
Result<bool> walk_path(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
Result<bool> walk_path_subdir(string &path, DIR *dir, const WalkFunction &func) {
while (true) {
errno = 0;
auto *entry = readdir(dir);
auto readdir_errno = errno;
if (readdir_errno) {
return Status::PosixError(readdir_errno, "readdir");
}
if (entry == nullptr) {
return true;
}
Slice name = Slice(static_cast<const char *>(entry->d_name));
if (name == "." || name == "..") {
continue;
}
auto size = path.size();
if (path.back() != TD_DIR_SLASH) {
path += TD_DIR_SLASH;
}
path.append(name.begin(), name.size());
SCOPE_EXIT {
path.resize(size);
};
Result<bool> status = true;
#ifdef DT_DIR
if (entry->d_type == DT_UNKNOWN) {
status = walk_path(path, func);
} else if (entry->d_type == DT_DIR) {
status = walk_path_dir(path, func);
} else if (entry->d_type == DT_REG) {
status = walk_path_file(path, func);
}
#else
#warning "Slow walk_path"
status = walk_path(path, func);
#endif
if (status.is_error() || !status.ok()) {
return status;
}
}
}
Result<bool> walk_path_dir(string &path, DIR *subdir, const WalkFunction &func) {
SCOPE_EXIT {
closedir(subdir);
};
switch (func(path, WalkPath::Type::EnterDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
return true;
case WalkPath::Action::Continue:
break;
}
auto status = walk_path_subdir(path, subdir, func);
if (status.is_error() || !status.ok()) {
return status;
}
switch (func(path, WalkPath::Type::ExitDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
case WalkPath::Action::Continue:
break;
}
return true;
}
Result<bool> walk_path_dir(string &path, FileFd fd, const WalkFunction &func) {
auto native_fd = fd.move_as_native_fd();
auto *subdir = fdopendir(native_fd.fd());
if (subdir == nullptr) {
return OS_ERROR("fdopendir");
}
native_fd.release();
return walk_path_dir(path, subdir, func);
}
Result<bool> walk_path_dir(string &path, const WalkFunction &func) {
auto *subdir = opendir(path.c_str());
if (subdir == nullptr) {
return OS_ERROR(PSLICE() << tag("opendir", path));
}
return walk_path_dir(path, subdir, func);
}
Result<bool> walk_path_file(string &path, const WalkFunction &func) {
switch (func(path, WalkPath::Type::NotDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
case WalkPath::Action::Continue:
break;
}
return true;
}
Result<bool> walk_path(string &path, const WalkFunction &func) {
TRY_RESULT(fd, FileFd::open(path, FileFd::Read));
TRY_RESULT(stat, fd.stat());
bool is_dir = stat.is_dir_;
bool is_reg = stat.is_reg_;
if (is_dir) {
return walk_path_dir(path, std::move(fd), func);
}
fd.close();
if (is_reg) {
return walk_path_file(path, func);
}
return true;
}
} // namespace detail
Status WalkPath::do_run(CSlice path, const detail::WalkFunction &func) {
string curr_path;
curr_path.reserve(PATH_MAX + 10);
curr_path = path.c_str();
TRY_STATUS(detail::walk_path(curr_path, func));
return Status::OK();
}
#endif
#if TD_PORT_WINDOWS
Status mkdir(CSlice dir, int32 mode) {
TRY_RESULT(wdir, to_wstring(dir));
auto status = CreateDirectoryW(wdir.c_str(), nullptr);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
return OS_ERROR(PSLICE() << "Can't create directory \"" << dir << '"');
}
return Status::OK();
}
Status rename(CSlice from, CSlice to) {
TRY_RESULT(wfrom, to_wstring(from));
TRY_RESULT(wto, to_wstring(to));
auto status = MoveFileExW(wfrom.c_str(), wto.c_str(), MOVEFILE_REPLACE_EXISTING);
if (status == 0) {
return OS_ERROR(PSLICE() << "Can't rename \"" << from << "\" to \"" << to << '\"');
}
return Status::OK();
}
Result<string> realpath(CSlice slice, bool ignore_access_denied) {
wchar_t buf[MAX_PATH + 1];
TRY_RESULT(wslice, to_wstring(slice));
auto status = GetFullPathNameW(wslice.c_str(), MAX_PATH, buf, nullptr);
string res;
if (status == 0) {
if (ignore_access_denied && errno == ERROR_ACCESS_DENIED) {
res = slice.str();
} else {
return OS_ERROR(PSLICE() << "GetFullPathNameW failed for \"" << slice << '"');
}
} else {
TRY_RESULT(t_res, from_wstring(buf));
res = std::move(t_res);
}
if (res.empty()) {
return Status::Error("Empty path");
}
if (!slice.empty() && slice.end()[-1] == TD_DIR_SLASH) {
if (res.back() != TD_DIR_SLASH) {
res += TD_DIR_SLASH;
}
}
return res;
}
Status chdir(CSlice dir) {
TRY_RESULT(wdir, to_wstring(dir));
auto res = SetCurrentDirectoryW(wdir.c_str());
if (res == 0) {
return OS_ERROR(PSLICE() << "Can't change directory to \"" << dir << '"');
}
return Status::OK();
}
Status rmdir(CSlice dir) {
TRY_RESULT(wdir, to_wstring(dir));
int status = RemoveDirectoryW(wdir.c_str());
if (!status) {
return OS_ERROR(PSLICE() << "Can't delete directory \"" << dir << '"');
}
return Status::OK();
}
Status unlink(CSlice path) {
TRY_RESULT(wpath, to_wstring(path));
int status = DeleteFileW(wpath.c_str());
if (!status) {
return OS_ERROR(PSLICE() << "Can't unlink \"" << path << '"');
}
return Status::OK();
}
CSlice get_temporary_dir() {
static bool is_inited = [] {
if (temporary_dir.empty()) {
wchar_t buf[MAX_PATH + 1];
if (GetTempPathW(MAX_PATH, buf) == 0) {
auto error = OS_ERROR("GetTempPathW failed");
LOG(FATAL) << error;
}
auto rs = from_wstring(buf);
LOG_IF(FATAL, rs.is_error()) << "GetTempPathW failed: " << rs.error();
temporary_dir = rs.ok();
}
if (temporary_dir.size() > 1 && temporary_dir.back() == TD_DIR_SLASH) {
temporary_dir.pop_back();
}
return true;
}();
LOG_IF(FATAL, !is_inited) << "Can't find temporary directory";
return temporary_dir;
}
Result<string> mkdtemp(CSlice dir, Slice prefix) {
if (dir.empty()) {
dir = get_temporary_dir();
if (dir.empty()) {
return Status::Error("Can't find temporary directory");
}
}
TRY_RESULT(dir_real, realpath(dir));
CHECK(!dir_real.empty());
string dir_pattern;
dir_pattern.reserve(dir_real.size() + prefix.size() + 7);
dir_pattern = dir_real;
if (dir_pattern.back() != TD_DIR_SLASH) {
dir_pattern += TD_DIR_SLASH;
}
dir_pattern.append(prefix.begin(), prefix.size());
for (auto iter = 0; iter < 20; iter++) {
auto path = dir_pattern;
for (int i = 0; i < 6 + iter / 5; i++) {
path += static_cast<char>(Random::fast('a', 'z'));
}
auto status = mkdir(path);
if (status.is_ok()) {
return path;
}
}
return Status::Error(PSLICE() << "Can't create temporary directory \"" << dir_pattern << '"');
}
Result<std::pair<FileFd, string>> mkstemp(CSlice dir) {
if (dir.empty()) {
dir = get_temporary_dir();
if (dir.empty()) {
return Status::Error("Can't find temporary directory");
}
}
TRY_RESULT(dir_real, realpath(dir));
CHECK(!dir_real.empty());
string file_pattern;
file_pattern.reserve(dir_real.size() + 14);
file_pattern = dir_real;
if (file_pattern.back() != TD_DIR_SLASH) {
file_pattern += TD_DIR_SLASH;
}
file_pattern += "tmp";
for (auto iter = 0; iter < 20; iter++) {
auto path = file_pattern;
for (int i = 0; i < 6 + iter / 5; i++) {
path += static_cast<char>(Random::fast('a', 'z'));
}
auto r_file = FileFd::open(path, FileFd::Write | FileFd::Read | FileFd::CreateNew);
if (r_file.is_ok()) {
return std::make_pair(r_file.move_as_ok(), path);
}
}
return Status::Error(PSLICE() << "Can't create temporary file \"" << file_pattern << '"');
}
static Result<bool> walk_path_dir(const std::wstring &dir_name,
const std::function<WalkPath::Action(CSlice name, WalkPath::Type type)> &func) {
std::wstring name = dir_name + L"\\*";
WIN32_FIND_DATA file_data;
auto handle = FindFirstFileExW(name.c_str(), FindExInfoStandard, &file_data, FindExSearchNameMatch, nullptr, 0);
if (handle == INVALID_HANDLE_VALUE) {
return OS_ERROR(PSLICE() << "FindFirstFileEx" << tag("name", from_wstring(name).ok()));
}
SCOPE_EXIT {
FindClose(handle);
};
TRY_RESULT(dir_entry_name, from_wstring(dir_name));
switch (func(dir_entry_name, WalkPath::Type::EnterDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
return true;
case WalkPath::Action::Continue:
break;
}
while (true) {
auto full_name = dir_name + L"\\" + file_data.cFileName;
TRY_RESULT(entry_name, from_wstring(full_name));
if (file_data.cFileName[0] != '.') {
if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
TRY_RESULT(is_ok, walk_path_dir(full_name, func));
if (!is_ok) {
return false;
}
} else {
switch (func(entry_name, WalkPath::Type::NotDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
case WalkPath::Action::Continue:
break;
}
}
}
auto status = FindNextFileW(handle, &file_data);
if (status == 0) {
auto last_error = GetLastError();
if (last_error == ERROR_NO_MORE_FILES) {
break;
}
return OS_ERROR("FindNextFileW");
}
}
switch (func(dir_entry_name, WalkPath::Type::ExitDir)) {
case WalkPath::Action::Abort:
return false;
case WalkPath::Action::SkipDir:
case WalkPath::Action::Continue:
break;
}
return true;
}
Status WalkPath::do_run(CSlice path, const std::function<Action(CSlice name, Type)> &func) {
TRY_RESULT(wpath, to_wstring(path));
Slice path_slice = path;
while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) {
path_slice.remove_suffix(1);
wpath.pop_back();
}
TRY_STATUS(walk_path_dir(wpath, func));
return Status::OK();
}
#endif
} // namespace td

View file

@ -0,0 +1,84 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <functional>
#include <type_traits>
#include <utility>
namespace td {
Status mkdir(CSlice dir, int32 mode = 0700) TD_WARN_UNUSED_RESULT;
Status mkpath(CSlice path, int32 mode = 0700) TD_WARN_UNUSED_RESULT;
Status rename(CSlice from, CSlice to) TD_WARN_UNUSED_RESULT;
Result<string> realpath(CSlice slice, bool ignore_access_denied = false) TD_WARN_UNUSED_RESULT;
Status chdir(CSlice dir) TD_WARN_UNUSED_RESULT;
Status rmdir(CSlice dir) TD_WARN_UNUSED_RESULT;
Status unlink(CSlice path) TD_WARN_UNUSED_RESULT;
Status rmrf(CSlice path) TD_WARN_UNUSED_RESULT;
Status set_temporary_dir(CSlice dir) TD_WARN_UNUSED_RESULT;
CSlice get_temporary_dir();
Result<std::pair<FileFd, string>> mkstemp(CSlice dir) TD_WARN_UNUSED_RESULT;
Result<string> mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT;
class WalkPath {
public:
enum class Action { Continue, Abort, SkipDir };
enum class Type { EnterDir, ExitDir, NotDir };
template <class F, class R = decltype(std::declval<F>()("", Type::ExitDir))>
static TD_WARN_UNUSED_RESULT std::enable_if_t<std::is_same<R, Action>::value, Status> run(CSlice path, F &&func) {
return do_run(path, func);
}
template <class F, class R = decltype(std::declval<F>()("", Type::ExitDir))>
static TD_WARN_UNUSED_RESULT std::enable_if_t<!std::is_same<R, Action>::value, Status> run(CSlice path, F &&func) {
return do_run(path, [&](CSlice name, Type type) {
func(name, type);
return Action::Continue;
});
}
private:
static TD_WARN_UNUSED_RESULT Status do_run(CSlice path,
const std::function<WalkPath::Action(CSlice name, Type type)> &func);
};
// deprecated interface
template <class F>
TD_WARN_UNUSED_RESULT Status walk_path(CSlice path, F &&func) {
return WalkPath::run(path, func);
}
} // namespace td

View file

@ -0,0 +1,128 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
// clang-format off
/*** Platform macros ***/
#if defined(_WIN32) || defined(_WINDOWS) // _WINDOWS is defined by CMake
#if defined(__cplusplus_winrt)
#define TD_WINRT 1
#endif
#if defined(__cplusplus_cli)
#define TD_CLI 1
#endif
#define TD_WINDOWS 1
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
// iOS/watchOS/tvOS
#if TARGET_OS_IOS
#define TD_DARWIN_IOS 1
#elif TARGET_OS_TV
#define TD_DARWIN_TV_OS 1
#elif TARGET_OS_WATCH
#define TD_DARWIN_WATCH_OS 1
#else
#warning "Probably unsupported Apple iPhone platform. Feel free to try to compile"
#endif
#elif TARGET_OS_MAC
// Other kinds of macOS
#define TD_DARWIN_MAC 1
#else
#warning "Probably unsupported Apple platform. Feel free to try to compile"
#endif
#define TD_DARWIN 1
#elif defined(ANDROID) || defined(__ANDROID__)
#define TD_ANDROID 1
#elif defined(TIZEN_DEPRECATION)
#define TD_TIZEN 1
#elif defined(__linux__)
#define TD_LINUX 1
#elif defined(__FreeBSD__)
#define TD_FREEBSD 1
#elif defined(__OpenBSD__)
#define TD_OPENBSD 1
#elif defined(__NetBSD__)
#define TD_NETBSD 1
#elif defined(__CYGWIN__)
#define TD_CYGWIN 1
#elif defined(__EMSCRIPTEN__)
#define TD_EMSCRIPTEN 1
#elif defined(__unix__) // all unices not caught above
#warning "Probably unsupported Unix platform. Feel free to try to compile"
#define TD_CYGWIN 1
#else
#error "Probably unsupported platform. Feel free to remove the error and try to recompile"
#endif
#if defined(__ICC) || defined(__INTEL_COMPILER)
#define TD_INTEL 1
#elif defined(__clang__)
#define TD_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
#define TD_GCC 1
#elif defined(_MSC_VER)
#define TD_MSVC 1
#else
#warning "Probably unsupported compiler. Feel free to try to compile"
#endif
#if TD_GCC || TD_CLANG || TD_INTEL
#define TD_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#define TD_ATTRIBUTE_FORMAT_PRINTF(from, to) __attribute__((format(printf, from, to)))
#else
#define TD_WARN_UNUSED_RESULT
#define TD_ATTRIBUTE_FORMAT_PRINTF(from, to)
#endif
#if TD_MSVC
#define TD_UNUSED __pragma(warning(suppress : 4100))
#elif TD_CLANG || TD_GCC || TD_INTEL
#define TD_UNUSED __attribute__((unused))
#else
#define TD_UNUSED
#endif
#define TD_HAVE_ATOMIC_SHARED_PTR 1
// No atomic operations on std::shared_ptr in libstdc++ before 5.0
// see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57250
#ifdef __GLIBCXX__
#undef TD_HAVE_ATOMIC_SHARED_PTR
#endif
// Also no atomic operations on std::shared_ptr when clang __has_feature(cxx_atomic) is defined and zero
#if defined(__has_feature)
#if !__has_feature(cxx_atomic)
#undef TD_HAVE_ATOMIC_SHARED_PTR
#endif
#endif
#ifdef TD_HAVE_ATOMIC_SHARED_PTR // unfortunately we can't check for __GLIBCXX__ here, it is not defined yet
#undef TD_HAVE_ATOMIC_SHARED_PTR
#endif
#define TD_CONCURRENCY_PAD 128
#if !TD_WINDOWS && defined(__SIZEOF_INT128__)
#define TD_HAVE_INT128 1
#endif
// clang-format on

View file

@ -0,0 +1,339 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/signals.h"
#include "td/utils/port/config.h"
#include "td/utils/port/stacktrace.h"
#include "td/utils/port/StdStreams.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#if TD_PORT_POSIX
#include <signal.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
#if TD_PORT_WINDOWS
#include <csignal>
#endif
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <limits>
namespace td {
#if TD_PORT_POSIX && !TD_DARWIN_TV_OS && !TD_DARWIN_WATCH_OS
static Status protect_memory(void *addr, size_t len) {
if (mprotect(addr, len, PROT_NONE) != 0) {
return OS_ERROR("mprotect failed");
}
return Status::OK();
}
#endif
Status setup_signals_alt_stack() {
#if TD_PORT_POSIX && !TD_DARWIN_TV_OS && !TD_DARWIN_WATCH_OS
auto page_size = getpagesize();
auto stack_size = (MINSIGSTKSZ + 16 * page_size - 1) / page_size * page_size;
void *stack = mmap(nullptr, stack_size + 2 * page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (stack == MAP_FAILED) {
return OS_ERROR("Mmap failed");
}
TRY_STATUS(protect_memory(stack, page_size));
TRY_STATUS(protect_memory(static_cast<char *>(stack) + stack_size + page_size, page_size));
stack_t signal_stack;
signal_stack.ss_sp = static_cast<char *>(stack) + page_size;
signal_stack.ss_size = stack_size;
signal_stack.ss_flags = 0;
if (sigaltstack(&signal_stack, nullptr) != 0) {
return OS_ERROR("sigaltstack failed");
}
#endif
return Status::OK();
}
#if TD_PORT_POSIX
static void set_handler(struct sigaction &act, decltype(act.sa_handler) handler) {
act.sa_handler = handler;
}
static void set_handler(struct sigaction &act, decltype(act.sa_sigaction) handler) {
act.sa_sigaction = handler;
act.sa_flags |= SA_SIGINFO;
}
template <class F>
static Status set_signal_handler_impl(vector<int> signals, F func) {
struct sigaction act;
std::memset(&act, '\0', sizeof(act));
sigemptyset(&act.sa_mask);
for (auto signal : signals) {
sigaddset(&act.sa_mask, signal);
}
act.sa_flags = SA_RESTART | SA_ONSTACK;
set_handler(act, func);
for (auto signal : signals) {
if (sigaction(signal, &act, nullptr) != 0) {
return OS_ERROR("sigaction failed");
}
}
return Status::OK();
}
static vector<int> get_native_signals(SignalType type) {
switch (type) {
case SignalType::Abort:
return {SIGABRT, SIGXCPU, SIGXFSZ};
case SignalType::Error:
return {SIGILL, SIGFPE, SIGBUS, SIGSEGV, SIGSYS};
case SignalType::Quit:
return {SIGINT, SIGTERM, SIGQUIT};
case SignalType::Pipe:
return {SIGPIPE};
case SignalType::HangUp:
return {SIGHUP};
case SignalType::User:
return {SIGUSR1, SIGUSR2};
case SignalType::Other:
return {SIGTRAP, SIGALRM, SIGVTALRM, SIGPROF, SIGTSTP, SIGTTIN, SIGTTOU};
default:
return {};
}
}
#endif
#if TD_PORT_WINDOWS
static Status set_signal_handler_impl(vector<int> signals, void (*func)(int sig)) {
for (auto signal : signals) {
if (std::signal(signal, func) == SIG_ERR) {
return Status::Error("Failed to set signal handler");
}
}
return Status::OK();
}
static vector<int> get_native_signals(SignalType type) {
switch (type) {
case SignalType::Abort:
return {SIGABRT};
case SignalType::Error:
return {SIGILL, SIGFPE, SIGSEGV};
case SignalType::Quit:
return {SIGINT, SIGTERM};
case SignalType::Pipe:
return {};
case SignalType::HangUp:
return {};
case SignalType::User:
return {};
case SignalType::Other:
return {};
default:
return {};
}
}
#endif
Status set_signal_handler(SignalType type, void (*func)(int)) {
return set_signal_handler_impl(get_native_signals(type), func == nullptr ? SIG_DFL : func);
}
using extended_signal_handler = void (*)(int sig, void *addr);
static extended_signal_handler extended_signal_handlers[NSIG] = {};
#if TD_PORT_POSIX
static void siginfo_handler(int signum, siginfo_t *info, void *data) {
auto handler = extended_signal_handlers[signum];
handler(signum, info->si_addr);
}
#elif TD_PORT_WINDOWS
static void siginfo_handler(int signum) {
auto handler = extended_signal_handlers[signum];
handler(signum, nullptr);
}
#endif
Status set_extended_signal_handler(SignalType type, extended_signal_handler func) {
CHECK(func != nullptr);
auto signals = get_native_signals(type);
for (auto signal : signals) {
if (0 <= signal && signal < NSIG) {
extended_signal_handlers[signal] = func;
} else {
UNREACHABLE();
}
}
return set_signal_handler_impl(std::move(signals), siginfo_handler);
}
Status set_runtime_signal_handler(int runtime_signal_number, void (*func)(int)) {
#ifdef SIGRTMIN
CHECK(SIGRTMIN + runtime_signal_number <= SIGRTMAX);
return set_signal_handler_impl({SIGRTMIN + runtime_signal_number}, func == nullptr ? SIG_DFL : func);
#else
return Status::OK();
#endif
}
Status ignore_signal(SignalType type) {
return set_signal_handler_impl(get_native_signals(type), SIG_IGN);
}
static void signal_safe_append_int(char **s, Slice name, int number) {
if (number < 0) {
number = std::numeric_limits<int>::max();
}
*--*s = ' ';
*--*s = ']';
do {
*--*s = static_cast<char>(number % 10 + '0');
number /= 10;
} while (number > 0);
*--*s = ' ';
for (auto pos = static_cast<int>(name.size()) - 1; pos >= 0; pos--) {
*--*s = name[pos];
}
*--*s = '[';
}
static void signal_safe_write_data(Slice data) {
#if TD_PORT_POSIX
while (!data.empty()) {
auto res = write(2, data.begin(), data.size());
if (res < 0 && errno == EINTR) {
continue;
}
if (res <= 0) {
break;
}
if (res > 0) {
data.remove_prefix(res);
}
}
#elif TD_PORT_WINDOWS
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
DWORD bytes_written;
WriteFile(stderr_handle, data.data(), static_cast<DWORD>(data.size()), &bytes_written, nullptr);
#else
// there is no stderr
#endif
#endif
}
static int get_process_id() {
#if TD_PORT_POSIX
return getpid();
#elif TD_PORT_WINDOWS
return GetCurrentProcessId();
#endif
}
void signal_safe_write(Slice data, bool add_header) {
auto old_errno = errno;
if (add_header) {
constexpr size_t HEADER_BUF_SIZE = 100;
char header[HEADER_BUF_SIZE];
char *header_end = header + HEADER_BUF_SIZE;
char *header_begin = header_end;
signal_safe_append_int(&header_begin, "time", static_cast<int>(std::time(nullptr)));
signal_safe_append_int(&header_begin, "pid", get_process_id());
signal_safe_write_data(Slice(header_begin, header_end));
}
signal_safe_write_data(data);
errno = old_errno;
}
void signal_safe_write_signal_number(int sig, bool add_header) {
char buf[100];
char *end = buf + sizeof(buf);
char *ptr = end;
*--ptr = '\n';
do {
*--ptr = static_cast<char>(sig % 10 + '0');
sig /= 10;
} while (sig != 0);
ptr -= 8;
std::memcpy(ptr, "Signal: ", 8);
signal_safe_write(Slice(ptr, end), add_header);
}
void signal_safe_write_pointer(void *p, bool add_header) {
std::uintptr_t addr = reinterpret_cast<std::uintptr_t>(p);
char buf[100];
char *end = buf + sizeof(buf);
char *ptr = end;
*--ptr = '\n';
do {
*--ptr = td::format::hex_digit(addr % 16);
addr /= 16;
} while (addr != 0);
*--ptr = 'x';
*--ptr = '0';
ptr -= 9;
std::memcpy(ptr, "Address: ", 9);
signal_safe_write(Slice(ptr, end), add_header);
}
static void block_stdin() {
#if TD_PORT_POSIX
Stdin().get_native_fd().set_is_blocking(true).ignore();
#endif
}
static void default_failure_signal_handler(int sig) {
signal_safe_write_signal_number(sig);
Stacktrace::PrintOptions options;
options.use_gdb = true;
Stacktrace::print_to_stderr(options);
block_stdin();
_Exit(EXIT_FAILURE);
}
Status set_default_failure_signal_handler() {
std::atexit(block_stdin);
TRY_STATUS(setup_signals_alt_stack());
TRY_STATUS(set_signal_handler(SignalType::Abort, default_failure_signal_handler));
TRY_STATUS(set_signal_handler(SignalType::Error, default_failure_signal_handler));
return Status::OK();
}
} // namespace td

View file

@ -0,0 +1,48 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
Status setup_signals_alt_stack() TD_WARN_UNUSED_RESULT;
enum class SignalType { Abort, Error, Quit, Pipe, HangUp, User, Other };
Status set_signal_handler(SignalType type, void (*func)(int sig)) TD_WARN_UNUSED_RESULT;
Status set_extended_signal_handler(SignalType type, void (*func)(int sig, void *addr)) TD_WARN_UNUSED_RESULT;
Status set_runtime_signal_handler(int runtime_signal_number, void (*func)(int sig)) TD_WARN_UNUSED_RESULT;
Status ignore_signal(SignalType type) TD_WARN_UNUSED_RESULT;
// writes data to the standard error stream in a signal-safe way
void signal_safe_write(Slice data, bool add_header = true);
void signal_safe_write_signal_number(int sig, bool add_header = true);
void signal_safe_write_pointer(void *p, bool add_header = true);
Status set_default_failure_signal_handler();
} // namespace td

View file

@ -0,0 +1,49 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/sleep.h"
#include "td/utils/port/config.h"
#if TD_PORT_POSIX
#if _POSIX_C_SOURCE >= 199309L
#include <time.h>
#else
#include <unistd.h>
#endif
#endif
namespace td {
void usleep_for(int32 microseconds) {
#if TD_PORT_WINDOWS
int32 milliseconds = microseconds / 1000 + (microseconds % 1000 ? 1 : 0);
Sleep(milliseconds);
#else
#if _POSIX_C_SOURCE >= 199309L
timespec ts;
ts.tv_sec = microseconds / 1000000;
ts.tv_nsec = (microseconds % 1000000) * 1000;
nanosleep(&ts, nullptr);
#else
usleep(microseconds);
#endif
#endif
}
} // namespace td

View file

@ -0,0 +1,27 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
namespace td {
void usleep_for(int32 microseconds);
} // namespace td

View file

@ -0,0 +1,129 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/stacktrace.h"
#include "td/utils/port/signals.h"
#if __GLIBC__
#include <execinfo.h>
#endif
#if TD_LINUX || TD_FREEBSD
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if TD_LINUX
#include <sys/prctl.h>
#endif
#endif
namespace td {
namespace {
void print_backtrace(void) {
#if __GLIBC__
void *buffer[128];
int nptrs = backtrace(buffer, 128);
signal_safe_write("------- Stack Backtrace -------\n", false);
backtrace_symbols_fd(buffer, nptrs, 2);
signal_safe_write("-------------------------------\n", false);
#endif
}
void print_backtrace_gdb(void) {
#if TD_LINUX || TD_FREEBSD
char pid_buf[30];
char *pid_buf_begin = pid_buf + sizeof(pid_buf);
pid_t pid = getpid();
*--pid_buf_begin = '\0';
do {
*--pid_buf_begin = static_cast<char>(pid % 10 + '0');
pid /= 10;
} while (pid > 0);
char name_buf[512];
ssize_t res = readlink("/proc/self/exe", name_buf, 511); // TODO works only under Linux
if (res >= 0) {
name_buf[res] = 0;
#if TD_LINUX
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
signal_safe_write("Can't set dumpable\n");
return;
}
#if defined(PR_SET_PTRACER)
// We can't use event fd because we are in a signal handler
int fds[2];
bool need_set_ptracer = true;
if (pipe(fds) < 0) {
need_set_ptracer = false;
signal_safe_write("Can't create a pipe\n");
}
#endif
#endif
int child_pid = fork();
if (child_pid < 0) {
signal_safe_write("Can't fork() to run gdb\n");
return;
}
if (!child_pid) {
#if TD_LINUX && defined(PR_SET_PTRACER)
if (need_set_ptracer) {
char c;
if (read(fds[0], &c, 1) < 0) {
signal_safe_write("Failed to read from pipe\n");
}
}
#endif
dup2(2, 1); // redirect output to stderr
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "thread apply all bt full", name_buf, pid_buf_begin,
nullptr);
return;
} else {
#if TD_LINUX && defined(PR_SET_PTRACER)
if (need_set_ptracer) {
if (prctl(PR_SET_PTRACER, child_pid, 0, 0, 0) < 0) {
signal_safe_write("Can't set ptracer\n");
}
if (write(fds[1], "a", 1) != 1) {
signal_safe_write("Can't write to pipe\n");
}
}
#endif
waitpid(child_pid, nullptr, 0);
}
} else {
signal_safe_write("Can't get name of executable file to pass to gdb\n");
}
#endif
}
} // namespace
void Stacktrace::print_to_stderr(const PrintOptions &options) {
if (options.use_gdb) {
print_backtrace_gdb();
}
print_backtrace();
}
} // namespace td

View file

@ -0,0 +1,33 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
namespace td {
class Stacktrace {
public:
struct PrintOptions {
bool use_gdb = false;
PrintOptions() {
}
};
static void print_to_stderr(const PrintOptions &options = PrintOptions());
};
} // namespace td

View file

@ -0,0 +1,46 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/detail/ThreadPthread.h"
#include "td/utils/port/detail/ThreadStl.h"
namespace td {
// clang-format off
#if TD_THREAD_PTHREAD
using thread = detail::ThreadPthread;
namespace this_thread = detail::this_thread_pthread;
#elif TD_THREAD_STL
using thread = detail::ThreadStl;
namespace this_thread = detail::this_thread_stl;
#elif TD_THREAD_UNSUPPORTED
namespace this_thread {
inline void yield() {}
}
#else
#error "Thread's implementation is not defined"
#endif
// clang-format on
} // namespace td

View file

@ -0,0 +1,53 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/thread_local.h"
namespace td {
namespace detail {
static TD_THREAD_LOCAL int32 thread_id_;
static TD_THREAD_LOCAL std::vector<unique_ptr<Destructor>> *thread_local_destructors;
void add_thread_local_destructor(unique_ptr<Destructor> destructor) {
if (thread_local_destructors == nullptr) {
thread_local_destructors = new std::vector<unique_ptr<Destructor>>();
}
thread_local_destructors->push_back(std::move(destructor));
}
} // namespace detail
void clear_thread_locals() {
// ensure that no destructors were added during destructors invokation
auto to_delete = detail::thread_local_destructors;
detail::thread_local_destructors = nullptr;
delete to_delete;
CHECK(detail::thread_local_destructors == nullptr);
}
void set_thread_id(int32 id) {
detail::thread_id_ = id;
}
int32 get_thread_id() {
return detail::thread_id_;
}
} // namespace td

View file

@ -0,0 +1,77 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/Destructor.h"
#include <memory>
#include <utility>
namespace td {
// clang-format off
#if TD_GCC || TD_CLANG
#define TD_THREAD_LOCAL __thread
#elif TD_INTEL || TD_MSVC
#define TD_THREAD_LOCAL thread_local
#else
#warning "TD_THREAD_LOCAL is not defined, trying 'thread_local'"
#define TD_THREAD_LOCAL thread_local
#endif
// clang-format on
// If raw_ptr is not nullptr, allocate T as in std::make_unique<T>(args...) and store pointer into raw_ptr
template <class T, class P, class... ArgsT>
bool init_thread_local(P &raw_ptr, ArgsT &&... args);
// Destroy all thread locals, and store nullptr into corresponding pointers
void clear_thread_locals();
void set_thread_id(int32 id);
int32 get_thread_id();
namespace detail {
void add_thread_local_destructor(unique_ptr<Destructor> destructor);
template <class T, class P, class... ArgsT>
void do_init_thread_local(P &raw_ptr, ArgsT &&... args) {
auto ptr = std::make_unique<T>(std::forward<ArgsT>(args)...);
raw_ptr = ptr.get();
detail::add_thread_local_destructor(create_destructor([ptr = std::move(ptr), &raw_ptr]() mutable {
ptr.reset();
raw_ptr = nullptr;
}));
}
} // namespace detail
template <class T, class P, class... ArgsT>
bool init_thread_local(P &raw_ptr, ArgsT &&... args) {
if (likely(raw_ptr != nullptr)) {
return false;
}
detail::do_init_thread_local<T>(raw_ptr, std::forward<ArgsT>(args)...);
return true;
}
} // namespace td

View file

@ -0,0 +1,41 @@
#include "user.h"
#if TD_LINUX
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#endif
namespace td {
#if TD_LINUX
td::Status change_user(td::Slice user) {
struct passwd *pw;
if (getuid() != 0 || geteuid() != 0) {
return td::Status::PosixError(errno, "cannot setuid() as not root");
}
if ((pw = getpwnam(user.str().c_str())) == 0) {
return td::Status::PosixError(errno, PSTRING() << "bad user '" << user << "'");
}
gid_t gid = pw->pw_gid;
if (setgroups(1, &gid) < 0) {
return td::Status::PosixError(errno, "failed to clear supplementary groups list");
}
if (initgroups(user.str().c_str(), gid) != 0) {
return td::Status::PosixError(errno, "failed to load groups of user");
}
if (setgid(pw->pw_gid) < 0) {
return td::Status::PosixError(errno, "failed to setgid()");
}
if (setuid(pw->pw_uid) < 0) {
return td::Status::PosixError(errno, "failed to setuid()");
}
return td::Status::OK();
}
#else
td::Status change_user(td::Slice username) {
return td::Status::Error("not implemented");
}
#endif
} // namespace td

View file

@ -0,0 +1,11 @@
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/platform.h"
#include "td/utils/Status.h"
namespace td {
td::Status change_user(td::Slice username);
}

View file

@ -0,0 +1,127 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "td/utils/port/wstring_convert.h"
char disable_linker_warning_about_empty_file_wstring_convert_cpp TD_UNUSED;
#if TD_PORT_WINDOWS
#include "td/utils/utf8.h"
#include <cwchar>
namespace td {
Result<std::wstring> to_wstring(CSlice slice) {
if (!check_utf8(slice)) {
return Status::Error("Wrong encoding");
}
size_t wstring_len = 0;
for (auto c : slice) {
wstring_len += ((c & 0xc0) != 0x80) + ((c & 0xf8) == 0xf0);
}
std::wstring result(wstring_len, static_cast<wchar_t>(0));
if (wstring_len) {
wchar_t *res = &result[0];
for (size_t i = 0; i < slice.size();) {
unsigned int a = static_cast<unsigned char>(slice[i++]);
if (a >= 0x80) {
unsigned int b = static_cast<unsigned char>(slice[i++]);
if (a >= 0xe0) {
unsigned int c = static_cast<unsigned char>(slice[i++]);
if (a >= 0xf0) {
unsigned int d = static_cast<unsigned char>(slice[i++]);
unsigned int val = ((a & 0x07) << 18) + ((b & 0x3f) << 12) + ((c & 0x3f) << 6) + (d & 0x3f) - 0x10000;
*res++ = static_cast<wchar_t>(0xD800 + (val >> 10));
*res++ = static_cast<wchar_t>(0xDC00 + (val & 0x3ff));
} else {
*res++ = static_cast<wchar_t>(((a & 0x0f) << 12) + ((b & 0x3f) << 6) + (c & 0x3f));
}
} else {
*res++ = static_cast<wchar_t>(((a & 0x1f) << 6) + (b & 0x3f));
}
} else {
*res++ = static_cast<wchar_t>(a);
}
}
CHECK(res == &result[0] + wstring_len);
}
return result;
}
Result<string> from_wstring(const wchar_t *begin, size_t size) {
size_t result_len = 0;
for (size_t i = 0; i < size; i++) {
unsigned int cur = begin[i];
if ((cur & 0xF800) == 0xD800) {
if (i < size) {
unsigned int next = begin[++i];
if ((next & 0xFC00) == 0xDC00 && (cur & 0x400) == 0) {
result_len += 4;
continue;
}
}
return Status::Error("Wrong encoding");
}
result_len += 1 + (cur >= 0x80) + (cur >= 0x800);
}
std::string result(result_len, '\0');
if (result_len) {
char *res = &result[0];
for (size_t i = 0; i < size; i++) {
unsigned int cur = begin[i];
// TODO conversion unsigned int -> signed char is implementation defined
if (cur <= 0x7f) {
*res++ = static_cast<char>(cur);
} else if (cur <= 0x7ff) {
*res++ = static_cast<char>(0xc0 | (cur >> 6));
*res++ = static_cast<char>(0x80 | (cur & 0x3f));
} else if ((cur & 0xF800) != 0xD800) {
*res++ = static_cast<char>(0xe0 | (cur >> 12));
*res++ = static_cast<char>(0x80 | ((cur >> 6) & 0x3f));
*res++ = static_cast<char>(0x80 | (cur & 0x3f));
} else {
unsigned int next = begin[++i];
unsigned int val = ((cur - 0xD800) << 10) + next - 0xDC00 + 0x10000;
*res++ = static_cast<char>(0xf0 | (val >> 18));
*res++ = static_cast<char>(0x80 | ((val >> 12) & 0x3f));
*res++ = static_cast<char>(0x80 | ((val >> 6) & 0x3f));
*res++ = static_cast<char>(0x80 | (val & 0x3f));
}
}
}
return result;
}
Result<string> from_wstring(const std::wstring &str) {
return from_wstring(str.data(), str.size());
}
Result<string> from_wstring(const wchar_t *begin) {
return from_wstring(begin, std::wcslen(begin));
}
} // namespace td
#endif

View file

@ -0,0 +1,43 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#if TD_PORT_WINDOWS
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <string>
namespace td {
Result<std::wstring> to_wstring(CSlice slice);
Result<string> from_wstring(const std::wstring &str);
Result<string> from_wstring(const wchar_t *begin, size_t size);
Result<string> from_wstring(const wchar_t *begin);
} // namespace td
#endif