diff --git a/CMakeLists.txt b/CMakeLists.txt index 14f290b..ba0c558 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,12 +5,15 @@ if (${CMAKE_VERSION} STREQUAL "4.1.1") set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") elseif (${CMAKE_VERSION} STREQUAL "4.0.2") set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457") +elseif (${CMAKE_VERSION} STREQUAL "4.2.0-rc2") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") else () message(FATAL_ERROR "Can't enable CMAKE_EXPERIMENTAL_CXX_IMPORT_STD for this version of cmake, please edit top-lvl CMakeLists.txt") endif() project(networks_1 LANGUAGES CXX) + set(CMP0155 NEW) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_SCAN_FOR_MODULES ON) @@ -26,4 +29,6 @@ add_subdirectory(modules/asyncio) add_subdirectory(programs/lab4) add_executable(main main.cpp) -target_link_libraries(main PRIVATE exceptions threads sockets streams) \ No newline at end of file +target_link_libraries(main PRIVATE exceptions threads sockets streams) + +add_executable(test-gcc gcc2.cpp) diff --git a/linux-gcc.dockerfile b/linux-gcc.dockerfile new file mode 100644 index 0000000..7b577d7 --- /dev/null +++ b/linux-gcc.dockerfile @@ -0,0 +1,13 @@ +FROM alpine:latest + +RUN apk update \ + && apk upgrade \ + && apk add gcompat ninja-build linux-headers \ + && apk --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main add gcc g++ gdb \ + && rm -rf /var/cache/apk/* + +RUN cd /root/ \ + && wget https://github.com/Kitware/CMake/releases/download/v4.2.0-rc2/cmake-4.2.0-rc2-linux-x86_64.sh \ + && sh cmake-4.2.0-rc2-linux-x86_64.sh --prefix=/opt/cmake --skip-license --include-subdir \ + && rm cmake-4.2.0-rc2-linux-x86_64.sh \ + && ln -s /opt/cmake/cmake-4.2.0-rc2-linux-x86_64/bin/cmake /usr/bin/cmake diff --git a/modules/exceptions/CMakeLists.txt b/modules/exceptions/CMakeLists.txt index 8a4d5c5..40865cc 100644 --- a/modules/exceptions/CMakeLists.txt +++ b/modules/exceptions/CMakeLists.txt @@ -7,4 +7,5 @@ target_sources( FILE_SET cxx_modules TYPE CXX_MODULES FILES ./src/exception.cppm ./src/windows.cppm + ./src/posix.cppm ) \ No newline at end of file diff --git a/modules/exceptions/src/posix.cppm b/modules/exceptions/src/posix.cppm new file mode 100644 index 0000000..0650aee --- /dev/null +++ b/modules/exceptions/src/posix.cppm @@ -0,0 +1,61 @@ +module; + +#ifdef __linux__ +#include +#include +#endif + + +export module ru.landgrafhomyak.BGTU.networks_1.exceptions.posix; + +#ifdef __linux__ + +// import std; +import ru.landgrafhomyak.BGTU.networks_1.exceptions; + +namespace LdH { + export + [[noreturn]] + void throwFromLinuxErrCode(int code); + + export + [[noreturn]] + void throwFromLastLinuxErr(); + + export void throwFromLastLinuxErrOrTimeout(); +} + +module : private; + +[[noreturn]] +void LdH::throwFromLinuxErrCode(int code) { + char msg[1024]; + // todo err check + strerror_r(code, msg, 1024); + size_t trim_size = std::strlen(msg); + while (trim_size > 0) { + switch (msg[--trim_size]) { + case '\n': + case '\r': + msg[trim_size] = '\0'; + continue; + default: + break; + } + break; + } +} + +[[noreturn]] +void LdH::throwFromLastLinuxErr() { + LdH::throwFromLinuxErrCode(errno); +} + +void LdH::throwFromLastLinuxErrOrTimeout() { + int err = errno; + if (err == ETIMEDOUT) + return; + LdH::throwFromLinuxErrCode(err); +} + +#endif diff --git a/modules/sockets/src/common.cppm b/modules/sockets/src/common.cppm index abd6f17..9f1c7ca 100644 --- a/modules/sockets/src/common.cppm +++ b/modules/sockets/src/common.cppm @@ -1,7 +1,6 @@ module; - #if defined(_WIN32) # include "platform/windows.hpp" @@ -10,6 +9,14 @@ export module ru.landgrafhomyak.BGTU.networks_1.sockets; # include "platform/windows.cpp.inc" +#elif defined(__linux__) + +# include "platform/linux.hpp" + +export module ru.landgrafhomyak.BGTU.networks_1.sockets; + +# include "platform/linux.cpp.inc" + #endif -module:private; \ No newline at end of file +module:private; diff --git a/modules/sockets/src/platform/linux.cpp.inc b/modules/sockets/src/platform/linux.cpp.inc new file mode 100644 index 0000000..0e54385 --- /dev/null +++ b/modules/sockets/src/platform/linux.cpp.inc @@ -0,0 +1,221 @@ +import ru.landgrafhomyak.BGTU.networks_1.exceptions; +import ru.landgrafhomyak.BGTU.networks_1.exceptions.posix; +import ru.landgrafhomyak.BGTU.networks_1.streams; +import :berkeley; + +namespace LdH::Sockets { + export + void init_sockets() { + } + + export + void deinit_sockets() { + } + + + struct _PosixSockContext { + class address_t { + public: + sockaddr_storage _value; + + private: + address_t(sockaddr_storage value) : _value{value} { + } + + public: + address_t() = default; + + address_t(address_t const &) = default; + + address_t(address_t &&) = default; + + address_t &operator=(address_t const &) = default; + + address_t &operator=(address_t &&) = default; + + static address_t parse(char const *addr, char const *service) { + addrinfo *p; + int ret = getaddrinfo(addr, service, nullptr, &p); + if (0 != ret) { + LdH::throwFromLinuxErrCode(ret); + } + sockaddr_storage out; + memset(&out, 0, sizeof(sockaddr_storage)); + memcpy(&out, p->ai_addr, p->ai_addrlen); + freeaddrinfo(p); + return address_t{out}; + } + + std::string to_string() { + char buffer[1024]; + if (nullptr != inet_ntop(this->_value.ss_family, &(this->_value), buffer, sizeof(sockaddr_storage))) { + LdH::throwFromLastLinuxErr(); + } + + return std::string{buffer}; + } + }; + + using sock_type_t = int; + using proto_t = int; + + struct af { + public: + static int inet() { return AF_INET; } + static int inet6() { return AF_INET6; } + static int ipx() { return AF_IPX; } + }; + + struct sock_type { + public: + static int stream() { return SOCK_STREAM; } + static int dgram() { return SOCK_DGRAM; } + }; + + struct proto { + public: + static int tcp() { return IPPROTO_TCP; } + static int udp() { return IPPROTO_UDP; } + static int icmp() { return IPPROTO_ICMP; } + }; + + class socket_t { + private: + int _value; + + explicit socket_t(int value) : _value{value} { + } + + public: + socket_t() : _value{-1} { + } + + socket_t(socket_t &&other) noexcept : _value{other._value} { + other._value = -1; + } + + socket_t &operator=(socket_t &&other) noexcept { + this->_value = other._value; + other._value = -1; + return *this; + } + + ~socket_t() = default; + + static socket_t create(address_t const &addr, int type, int proto) { + int sock = ::socket(addr._value.ss_family, type, proto); + if (sock == -1) { + LdH::throwFromLastLinuxErr(); + } + + return socket_t{sock}; + } + + void close() { + if (0 != ::close(this->_value)) { + LdH::throwFromLastLinuxErr(); + } + this->_value = -1; + } + + void connect(address_t addr) { + if (0 != ::connect(this->_value, reinterpret_cast(&(addr._value)), sizeof(sockaddr))) + LdH::throwFromLastLinuxErr(); + } + + void bind(address_t addr) { + if (0 != ::bind(this->_value, reinterpret_cast(&(addr._value)), sizeof(sockaddr))) + LdH::throwFromLastLinuxErr(); + } + + socket_t accept(address_t *addr) { + int sock = ::accept(this->_value, reinterpret_cast(&(addr->_value)), nullptr); + if (sock == -1) { + LdH::throwFromLastLinuxErr(); + } + return socket_t{sock}; + } + + void listen(std::size_t queue_size) { + if (0 != ::listen(this->_value, queue_size)) { + LdH::throwFromLastLinuxErr(); + } + } + + + void send_stream(std::size_t size, char const *data) { + while (true) { + std::size_t sent_count = ::send(this->_value, data, size, 0); + if (sent_count == -1) { + LdH::throwFromLastLinuxErr(); + } + data += sent_count; + size -= sent_count; + if (size <= 0) return; + } + } + + void recv_stream(std::size_t size, char *data) { + while (true) { + std::size_t sent_count = ::recv(this->_value, data, size, 0); + if (sent_count == -1) { + LdH::throwFromLastLinuxErr(); + } + data += sent_count; + size -= sent_count; + if (size <= 0) return; + } + } + + void send_datagram(address_t dest, std::size_t size, char const *data) { + int sent_count = ::sendto(this->_value, data, size, 0, reinterpret_cast(&dest._value), sizeof(dest._value)); + if (sent_count == -1) { + LdH::throwFromLastLinuxErr(); + } + } + + address_t recv_datagram(std::size_t size, char *data) { + address_t out; + socklen_t out_size = sizeof(out); + int sent_count = ::recvfrom(this->_value, data, size, 0, reinterpret_cast(&out), &out_size); + if (sent_count == -1) { + LdH::throwFromLastLinuxErr(); + } + return out; + } + }; + }; + + static_assert(Berkeley::BerkeleySocketsContext<_PosixSockContext>); + + + export using Address = Berkeley::Address<_PosixSockContext>; + + export using StreamSocket = Berkeley::StreamSocket<_PosixSockContext>; + + export using StreamSocketsServer = Berkeley::StreamSocketsServer<_PosixSockContext>; + + export + StreamSocket connect_tcp(Address addr) { + return Berkeley::connect_tcp<_PosixSockContext>(addr); + } + + export + StreamSocketsServer listen_tcp(Address addr, std::size_t queue_size) { + return Berkeley::listen_tcp<_PosixSockContext>(addr, queue_size); + } + + export using ClientDatagramSocket = Berkeley::ClientDatagramSocket<_PosixSockContext>; + + export using ServerDatagramSocket = Berkeley::ServerDatagramSocket<_PosixSockContext>; + + export + ClientDatagramSocket connect_udp(Address addr) { + return Berkeley::connect_udp<_PosixSockContext>(addr); + } + + export + ServerDatagramSocket listen_udp(Address addr) { + return Berkeley::listen_udp<_PosixSockContext>(addr); + } +} diff --git a/modules/sockets/src/platform/linux.hpp b/modules/sockets/src/platform/linux.hpp new file mode 100644 index 0000000..bc7871b --- /dev/null +++ b/modules/sockets/src/platform/linux.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include +#include + diff --git a/modules/threads/CMakeLists.txt b/modules/threads/CMakeLists.txt index 932b808..dea04db 100644 --- a/modules/threads/CMakeLists.txt +++ b/modules/threads/CMakeLists.txt @@ -9,6 +9,7 @@ target_sources( src/thread_routine.cppm src/highlevel_impl.cppm src/platform/windows.cppm + src/platform/posix.cppm ) target_link_libraries(threads PRIVATE exceptions Synchronization) \ No newline at end of file diff --git a/modules/threads/src/platform/posix.cppm b/modules/threads/src/platform/posix.cppm new file mode 100644 index 0000000..989ade4 --- /dev/null +++ b/modules/threads/src/platform/posix.cppm @@ -0,0 +1,111 @@ +module; + +#ifdef __linux__ + +#include +#include +#include +#include + +#include + +#endif + +export module ru.landgrafhomyak.BGTU.networks_1.threads:impl.posix; + +#ifdef __linux__ +import ru.landgrafhomyak.BGTU.networks_1.exceptions; +import ru.landgrafhomyak.BGTU.networks_1.exceptions.posix; +import :thread_routine; +import :highlevel_impl; + + +namespace LdH { + class NativeThreadWrapperForPosix { + private: + pthread_t _thread; + + public: + NativeThreadWrapperForPosix() : _thread{} { + } + + void join() { + auto ret = pthread_join(this->_thread, nullptr); + if (ret != 0) { + LdH::throwFromLinuxErrCode(ret); + } + } + + void destroy() { + auto ret = pthread_join(this->_thread, nullptr); + if (ret != 0) { + LdH::throwFromLinuxErrCode(ret); + } + } + + ~NativeThreadWrapperForPosix() = default; + + class notifier_t { + std::uint32_t _ptr; + + public: + notifier_t() : _ptr{0} { + } + + void wait() { + long ret = syscall(SYS_futex, &(this->_ptr), FUTEX_WAIT, 0, nullptr); + if (ret == -1) { + switch (errno) { + case EAGAIN: + return; + default: + LdH::throwFromLastLinuxErr(); + } + } + } + + void notify() { + long ret = syscall(SYS_futex, &(this->_ptr), FUTEX_WAIT, INT_MAX); + if (ret == -1) { + switch (errno) { + case EAGAIN: + return; + default: + LdH::throwFromLastLinuxErr(); + } + } + } + }; + + using kernel_ret_t = void *; + + static void *kernel_ret() { + return nullptr; + } + + private: + explicit NativeThreadWrapperForPosix(pthread_t hThread) : _thread{hThread} { + } + + public: + static NativeThreadWrapperForPosix start(std::string &, void * (*kernel)(void *), void *kernel_arg) { + pthread_t native_thread; + int ret = pthread_create(&native_thread, nullptr, kernel, kernel_arg); + if (ret != 0) { + LdH::throwFromLinuxErrCode(ret); + } + return NativeThreadWrapperForPosix{native_thread}; + } + }; + + static_assert(LdH::NativeHighLevelThreadWrapper); + + export + using ThreadController = LdH::CommonHighLevelThreadWrapper; + + export ThreadController fork(std::string &&name, ThreadRoutine auto &&routine) { + return _common_highlevel_fork(std::move(name), std::move(routine)); + } +} + +#endif