mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
35
tdutils/td/utils/port/Clocks.cpp
Normal file
35
tdutils/td/utils/port/Clocks.cpp
Normal 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
|
40
tdutils/td/utils/port/Clocks.h
Normal file
40
tdutils/td/utils/port/Clocks.h
Normal 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
|
159
tdutils/td/utils/port/CxCli.h
Normal file
159
tdutils/td/utils/port/CxCli.h
Normal 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
|
45
tdutils/td/utils/port/EventFd.h
Normal file
45
tdutils/td/utils/port/EventFd.h
Normal 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
|
44
tdutils/td/utils/port/EventFdBase.h
Normal file
44
tdutils/td/utils/port/EventFdBase.h
Normal 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
|
587
tdutils/td/utils/port/FileFd.cpp
Normal file
587
tdutils/td/utils/port/FileFd.cpp
Normal 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
|
87
tdutils/td/utils/port/FileFd.h
Normal file
87
tdutils/td/utils/port/FileFd.h
Normal 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
|
622
tdutils/td/utils/port/IPAddress.cpp
Normal file
622
tdutils/td/utils/port/IPAddress.cpp
Normal 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
|
100
tdutils/td/utils/port/IPAddress.h
Normal file
100
tdutils/td/utils/port/IPAddress.h
Normal 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
|
54
tdutils/td/utils/port/IoSlice.h
Normal file
54
tdutils/td/utils/port/IoSlice.h
Normal 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
|
123
tdutils/td/utils/port/MemoryMapping.cpp
Normal file
123
tdutils/td/utils/port/MemoryMapping.cpp
Normal 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
|
64
tdutils/td/utils/port/MemoryMapping.h
Normal file
64
tdutils/td/utils/port/MemoryMapping.h
Normal 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
|
47
tdutils/td/utils/port/Poll.h
Normal file
47
tdutils/td/utils/port/Poll.h
Normal 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
|
40
tdutils/td/utils/port/PollBase.h
Normal file
40
tdutils/td/utils/port/PollBase.h
Normal 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
|
83
tdutils/td/utils/port/PollFlags.cpp
Normal file
83
tdutils/td/utils/port/PollFlags.cpp
Normal 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
|
134
tdutils/td/utils/port/PollFlags.h
Normal file
134
tdutils/td/utils/port/PollFlags.h
Normal 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
|
161
tdutils/td/utils/port/RwMutex.h
Normal file
161
tdutils/td/utils/port/RwMutex.h
Normal 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
|
373
tdutils/td/utils/port/ServerSocketFd.cpp
Normal file
373
tdutils/td/utils/port/ServerSocketFd.cpp
Normal 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
|
66
tdutils/td/utils/port/ServerSocketFd.h
Normal file
66
tdutils/td/utils/port/ServerSocketFd.h
Normal 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
|
632
tdutils/td/utils/port/SocketFd.cpp
Normal file
632
tdutils/td/utils/port/SocketFd.cpp
Normal 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
|
82
tdutils/td/utils/port/SocketFd.h
Normal file
82
tdutils/td/utils/port/SocketFd.h
Normal 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
|
373
tdutils/td/utils/port/Stat.cpp
Normal file
373
tdutils/td/utils/port/Stat.cpp
Normal 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
|
65
tdutils/td/utils/port/Stat.h
Normal file
65
tdutils/td/utils/port/Stat.h
Normal 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
|
250
tdutils/td/utils/port/StdStreams.cpp
Normal file
250
tdutils/td/utils/port/StdStreams.cpp
Normal 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
|
61
tdutils/td/utils/port/StdStreams.h
Normal file
61
tdutils/td/utils/port/StdStreams.h
Normal 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
|
873
tdutils/td/utils/port/UdpSocketFd.cpp
Normal file
873
tdutils/td/utils/port/UdpSocketFd.cpp
Normal 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
|
105
tdutils/td/utils/port/UdpSocketFd.h
Normal file
105
tdutils/td/utils/port/UdpSocketFd.h
Normal 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
|
65
tdutils/td/utils/port/config.h
Normal file
65
tdutils/td/utils/port/config.h
Normal 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
|
136
tdutils/td/utils/port/detail/Epoll.cpp
Normal file
136
tdutils/td/utils/port/detail/Epoll.cpp
Normal 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
|
71
tdutils/td/utils/port/detail/Epoll.h
Normal file
71
tdutils/td/utils/port/detail/Epoll.h
Normal 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
|
114
tdutils/td/utils/port/detail/EventFdBsd.cpp
Normal file
114
tdutils/td/utils/port/detail/EventFdBsd.cpp
Normal 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
|
61
tdutils/td/utils/port/detail/EventFdBsd.h
Normal file
61
tdutils/td/utils/port/detail/EventFdBsd.h
Normal 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
|
138
tdutils/td/utils/port/detail/EventFdLinux.cpp
Normal file
138
tdutils/td/utils/port/detail/EventFdLinux.cpp
Normal 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
|
63
tdutils/td/utils/port/detail/EventFdLinux.h
Normal file
63
tdutils/td/utils/port/detail/EventFdLinux.h
Normal 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
|
80
tdutils/td/utils/port/detail/EventFdWindows.cpp
Normal file
80
tdutils/td/utils/port/detail/EventFdWindows.cpp
Normal 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
|
60
tdutils/td/utils/port/detail/EventFdWindows.h
Normal file
60
tdutils/td/utils/port/detail/EventFdWindows.h
Normal 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
|
122
tdutils/td/utils/port/detail/Iocp.cpp
Normal file
122
tdutils/td/utils/port/detail/Iocp.cpp
Normal 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
|
83
tdutils/td/utils/port/detail/Iocp.h
Normal file
83
tdutils/td/utils/port/detail/Iocp.h
Normal 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
|
192
tdutils/td/utils/port/detail/KQueue.cpp
Normal file
192
tdutils/td/utils/port/detail/KQueue.cpp
Normal 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
|
84
tdutils/td/utils/port/detail/KQueue.h
Normal file
84
tdutils/td/utils/port/detail/KQueue.h
Normal 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
|
259
tdutils/td/utils/port/detail/NativeFd.cpp
Normal file
259
tdutils/td/utils/port/detail/NativeFd.cpp
Normal 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
|
80
tdutils/td/utils/port/detail/NativeFd.h
Normal file
80
tdutils/td/utils/port/detail/NativeFd.h
Normal 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
|
114
tdutils/td/utils/port/detail/Poll.cpp
Normal file
114
tdutils/td/utils/port/detail/Poll.cpp
Normal 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
|
68
tdutils/td/utils/port/detail/Poll.h
Normal file
68
tdutils/td/utils/port/detail/Poll.h
Normal 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
|
262
tdutils/td/utils/port/detail/PollableFd.h
Normal file
262
tdutils/td/utils/port/detail/PollableFd.h
Normal 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
|
132
tdutils/td/utils/port/detail/Select.cpp
Normal file
132
tdutils/td/utils/port/detail/Select.cpp
Normal 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
|
76
tdutils/td/utils/port/detail/Select.h
Normal file
76
tdutils/td/utils/port/detail/Select.h
Normal 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
|
64
tdutils/td/utils/port/detail/ThreadIdGuard.cpp
Normal file
64
tdutils/td/utils/port/detail/ThreadIdGuard.cpp
Normal 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
|
38
tdutils/td/utils/port/detail/ThreadIdGuard.h
Normal file
38
tdutils/td/utils/port/detail/ThreadIdGuard.h
Normal 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
|
121
tdutils/td/utils/port/detail/ThreadPthread.h
Normal file
121
tdutils/td/utils/port/detail/ThreadPthread.h
Normal 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
|
89
tdutils/td/utils/port/detail/ThreadStl.h
Normal file
89
tdutils/td/utils/port/detail/ThreadStl.h
Normal 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
|
55
tdutils/td/utils/port/detail/WineventPoll.cpp
Normal file
55
tdutils/td/utils/port/detail/WineventPoll.cpp
Normal 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
|
62
tdutils/td/utils/port/detail/WineventPoll.h
Normal file
62
tdutils/td/utils/port/detail/WineventPoll.h
Normal 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
|
635
tdutils/td/utils/port/path.cpp
Normal file
635
tdutils/td/utils/port/path.cpp
Normal 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
|
84
tdutils/td/utils/port/path.h
Normal file
84
tdutils/td/utils/port/path.h
Normal 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
|
128
tdutils/td/utils/port/platform.h
Normal file
128
tdutils/td/utils/port/platform.h
Normal 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
|
339
tdutils/td/utils/port/signals.cpp
Normal file
339
tdutils/td/utils/port/signals.cpp
Normal 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
|
48
tdutils/td/utils/port/signals.h
Normal file
48
tdutils/td/utils/port/signals.h
Normal 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
|
49
tdutils/td/utils/port/sleep.cpp
Normal file
49
tdutils/td/utils/port/sleep.cpp
Normal 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
|
27
tdutils/td/utils/port/sleep.h
Normal file
27
tdutils/td/utils/port/sleep.h
Normal 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
|
129
tdutils/td/utils/port/stacktrace.cpp
Normal file
129
tdutils/td/utils/port/stacktrace.cpp
Normal 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
|
33
tdutils/td/utils/port/stacktrace.h
Normal file
33
tdutils/td/utils/port/stacktrace.h
Normal 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
|
46
tdutils/td/utils/port/thread.h
Normal file
46
tdutils/td/utils/port/thread.h
Normal 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
|
53
tdutils/td/utils/port/thread_local.cpp
Normal file
53
tdutils/td/utils/port/thread_local.cpp
Normal 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
|
77
tdutils/td/utils/port/thread_local.h
Normal file
77
tdutils/td/utils/port/thread_local.h
Normal 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
|
41
tdutils/td/utils/port/user.cpp
Normal file
41
tdutils/td/utils/port/user.cpp
Normal 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
|
11
tdutils/td/utils/port/user.h
Normal file
11
tdutils/td/utils/port/user.h
Normal 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);
|
||||
|
||||
}
|
127
tdutils/td/utils/port/wstring_convert.cpp
Normal file
127
tdutils/td/utils/port/wstring_convert.cpp
Normal 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
|
43
tdutils/td/utils/port/wstring_convert.h
Normal file
43
tdutils/td/utils/port/wstring_convert.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue