diff --git a/CMakeLists.txt b/CMakeLists.txt index 87a7650..7a4641c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") set(NETBSD TRUE) endif() +if(CMAKE_SYSTEM_NAME MATCHES "Haiku") + set(HAIKU TRUE) +endif() + if(APPLE) include_directories(SYSTEM /usr/include/malloc) enable_language(ASM) @@ -98,7 +102,7 @@ else() # gcc for powerpc64 doesn't like variable-length arrays allocated on stack add_definitions("-DFORCE_USE_HEAP") endif() - if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR HAIKU) # This option has no effect in glibc version less than 2.20. # Since glibc 2.20 _BSD_SOURCE is deprecated, this macro is recomended instead add_definitions("-D_DEFAULT_SOURCE" "-D_GNU_SOURCE") @@ -239,7 +243,7 @@ endif() if(MINGW) set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock") -elseif(APPLE OR OPENBSD OR ANDROID) +elseif(APPLE OR OPENBSD OR ANDROID OR HAIKU) set(Boost_LIBRARIES "${Boost_LIBRARIES}") elseif(NOT MSVC) set(Boost_LIBRARIES "${Boost_LIBRARIES};rt") @@ -294,7 +298,7 @@ else() endif() if(MINGW) set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) -elseif(APPLE OR OPENBSD OR ANDROID) +elseif(APPLE OR OPENBSD OR ANDROID OR HAIKU) set(EXTRA_LIBRARIES "") elseif(FREEBSD) set(EXTRA_LIBRARIES execinfo) diff --git a/external/miniupnpc/miniupnpc.c b/external/miniupnpc/miniupnpc.c index a68c926..06f25b2 100644 --- a/external/miniupnpc/miniupnpc.c +++ b/external/miniupnpc/miniupnpc.c @@ -17,7 +17,7 @@ #endif #endif -#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) +#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__HAIKU__) #define HAS_IP_MREQN #endif diff --git a/external/miniupnpc/portlistingparse.c b/external/miniupnpc/portlistingparse.c index 3ea9cb2..1c963b7 100644 --- a/external/miniupnpc/portlistingparse.c +++ b/external/miniupnpc/portlistingparse.c @@ -9,6 +9,11 @@ #include "portlistingparse.h" #include "minixml.h" +#if defined(__HAIKU__) +/* rename our private function because Haiku already defines a atoui() function */ +#define atoui atoui2 +#endif + /* list of the elements */ static const struct { const portMappingElt code; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c93cce..8e29336 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,8 @@ elseif(ANDROID) endif() elseif(FREEBSD OR NETBSD OR OPENBSD) file(GLOB_RECURSE System System/* Platform/FreeBSD/System/* Platform/Posix/System/*) +elseif(HAIKU) + file(GLOB_RECURSE System System/* Platform/Haiku/System/* Platform/Posix/System/*) else() file(GLOB_RECURSE System System/* Platform/Linux/System/* Platform/Posix/System/*) endif() diff --git a/src/Common/int-util.h b/src/Common/int-util.h index cd7bcf1..b81ed80 100644 --- a/src/Common/int-util.h +++ b/src/Common/int-util.h @@ -1,4 +1,5 @@ // Copyright (c) 2012-2017, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2024, The Talleo developers // // This file is part of Bytecoin. // @@ -23,6 +24,10 @@ #include #include +#if defined(__HAIKU__) +#include +#endif + #if defined(_MSC_VER) #include diff --git a/src/Platform/Haiku/System/Context.c b/src/Platform/Haiku/System/Context.c new file mode 100644 index 0000000..41491f8 --- /dev/null +++ b/src/Platform/Haiku/System/Context.c @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2022-2024, The Talleo developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include +#include +#include "Context.h" + +void +makecontext(uctx *ucp, void (*func)(void), intptr_t arg) +{ + long *sp; + + memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); + ucp->uc_mcontext.mc_rdi = (long)arg; + sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); + sp -= 1; + sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ + *--sp = 0; /* return address */ + ucp->uc_mcontext.mc_rip = (long)func; + ucp->uc_mcontext.mc_rsp = (long)sp; +} + +int +swapcontext(uctx *oucp, const uctx *ucp) +{ + if(getcontext(oucp) == 0) + setcontext(ucp); + return 0; +} diff --git a/src/Platform/Haiku/System/Context.h b/src/Platform/Haiku/System/Context.h new file mode 100644 index 0000000..393ed0e --- /dev/null +++ b/src/Platform/Haiku/System/Context.h @@ -0,0 +1,103 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#define setcontext(u) _setmcontext(&(u)->uc_mcontext) +#define getcontext(u) _getmcontext(&(u)->uc_mcontext) + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +typedef struct mcontext mctx; +typedef struct ucontext uctx; + +extern int swapcontext(uctx*, const uctx*); +extern void makecontext(uctx*, void(*)(void), intptr_t); +extern int _getmcontext(mctx*); +extern void _setmcontext(const mctx*); + +struct mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + long mc_onstack; /* XXX - sigcontext compat. */ + long mc_rdi; /* machine state (struct trapframe) */ + long mc_rsi; + long mc_rdx; + long mc_rcx; + long mc_r8; + long mc_r9; + long mc_rax; + long mc_rbx; + long mc_rbp; + long mc_r10; + long mc_r11; + long mc_r12; + long mc_r13; + long mc_r14; + long mc_r15; + long mc_trapno; + long mc_addr; + long mc_flags; + long mc_err; + long mc_rip; + long mc_cs; + long mc_rflags; + long mc_rsp; + long mc_ss; + + long mc_len; /* sizeof(mcontext_t) */ +#define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ +#define _MC_FPFMT_XMM 0x10002 + long mc_fpformat; +#define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ +#define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ +#define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ + long mc_ownedfp; + /* + * See for the internals of mc_fpstate[]. + */ + long mc_fpstate[64]; + long mc_spare[8]; +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mctx uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int __spare__[8]; +}; + +#ifdef __cplusplus +} +#endif diff --git a/src/Platform/Haiku/System/Dispatcher.cpp b/src/Platform/Haiku/System/Dispatcher.cpp new file mode 100644 index 0000000..df2b535 --- /dev/null +++ b/src/Platform/Haiku/System/Dispatcher.cpp @@ -0,0 +1,457 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2022-2024, The Talleo developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "Dispatcher.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Context.h" +#include "ErrorMessage.h" +#include + +namespace System { + +namespace{ + +struct ContextMakingData { + void* uctx; + Dispatcher* dispatcher; +}; + +class MutextGuard { +public: + MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) { + auto ret = pthread_mutex_lock(&mutex); + if (ret != 0) { + throw std::runtime_error("MutextGuard::MutextGuard, pthread_mutex_lock failed, " + errorMessage(ret)); + } + } + + ~MutextGuard() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t& mutex; +}; + +//const size_t STACK_SIZE = 64 * 1024; +const size_t STACK_SIZE = 512 * 1024; +} + +//static_assert(Dispatcher::SIZEOF_PTHREAD_MUTEX_T == sizeof(pthread_mutex_t), "invalid pthread mutex size"); + +Dispatcher::Dispatcher() : lastCreatedTimer(0) { + std::string message; + kqueue = ::kqueue(); + if (kqueue == -1) { + message = "kqueue failed, " + lastErrorMessage(); + } else { + mainContext.uctx = new uctx; + if (getcontext(static_cast(mainContext.uctx)) == -1) { + message = "getcontext failed, " + lastErrorMessage(); + } else { + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD, NOTE_FFNOP, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + if(pthread_mutex_init(reinterpret_cast(this->mutex), NULL) == -1) { + message = "pthread_mutex_init failed, " + lastErrorMessage(); + } else { + remoteSpawned = false; + + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + mainContext.inExecutionQueue = false; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; + return; + } + } + } + + auto result = close(kqueue); + assert(result == 0); + } + + throw std::runtime_error("Dispatcher::Dispatcher, " + message); +} + +Dispatcher::~Dispatcher() { + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); + } + + yield(); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; + } + + auto result = close(kqueue); + assert(result != -1); + result = pthread_mutex_destroy(reinterpret_cast(this->mutex)); + assert(result != -1); +} + +void Dispatcher::clear() { + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; + } +} + +void Dispatcher::dispatch() { + NativeContext* context; + for (;;) { + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; + //assert(context->inExecutionQueue); + context->inExecutionQueue = false; + break; + } + + if(remoteSpawned.load() == true) { + MutextGuard guard(*reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + remoteSpawned = false; + continue; + } + + struct kevent event; + int count = kevent(kqueue, NULL, 0, &event, 1, NULL); + if (count == 1) { + if (event.flags & EV_ERROR) { + continue; + } + + if (event.filter == EVFILT_USER && event.ident == 0) { + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Dispatcher::dispatch, kevent failed, " + lastErrorMessage()); + } + + continue; + } + + if (event.filter == EVFILT_WRITE) { + event.flags = EV_DELETE | EV_DISABLE; + kevent(kqueue, &event, 1, NULL, 0, NULL); // ignore error here + } + + context = static_cast(event.udata)->context; + break; + } + + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch, kqueue failed, " + lastErrorMessage()); + } else { + MutextGuard guard(*reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + } + } + + if (context != currentContext) { + uctx* oldContext = static_cast(currentContext->uctx); + currentContext = context; + if (swapcontext(oldContext,static_cast(currentContext->uctx)) == -1) { + throw std::runtime_error("Dispatcher::dispatch, swapcontext failed, " + lastErrorMessage()); + } + } +} + +NativeContext* Dispatcher::getCurrentContext() const { + return currentContext; +} + +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { + assert(context!=nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(context!=nullptr); + if (context->inExecutionQueue) + return; + context->next = nullptr; + context->inExecutionQueue = true; + if (firstResumingContext != nullptr) { + assert(lastResumingContext != nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; +} + +void Dispatcher::remoteSpawn(std::function&& procedure) { + MutextGuard guard(*reinterpret_cast(this->mutex)); + remoteSpawningProcedures.push(std::move(procedure)); + if (remoteSpawned == false) { + remoteSpawned = true; + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ENABLE, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Dispatcher::remoteSpawn, kevent failed, " + lastErrorMessage()); + }; + } +} + +void Dispatcher::spawn(std::function&& procedure) { + NativeContext* context = &getReusableContext(); + if(contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; + } else { + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; + } + + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); +} + +void Dispatcher::yield() { + struct timespec zeroTimeout = { 0, 0 }; + int updatesCounter = 0; + for (;;) { + struct kevent events[16]; + struct kevent updates[16]; + int count = kevent(kqueue, updates, updatesCounter, events, 16, &zeroTimeout); + if (count == 0) { + break; + } + + updatesCounter = 0; + if (count > 0) { + for (int i = 0; i < count; ++i) { + if (events[i].flags & EV_ERROR) { + continue; + } + + if (events[i].filter == EVFILT_USER && events[i].ident == 0) { + EV_SET(&updates[updatesCounter++], 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL); + + MutextGuard guard(*reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + remoteSpawned = false; + continue; + } + + static_cast(events[i].udata)->context->interruptProcedure = nullptr; + pushContext(static_cast(events[i].udata)->context); + if (events[i].filter == EVFILT_WRITE) { + EV_SET(&updates[updatesCounter++], events[i].ident, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + } + } + } else { + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch, kevent failed, " + lastErrorMessage()); + } + } + } + + if (firstResumingContext != nullptr) { + pushContext(currentContext); + dispatch(); + } +} + +int Dispatcher::getKqueue() const { + return kqueue; +} + +NativeContext& Dispatcher::getReusableContext() { + if(firstReusableContext == nullptr) { + uctx* newlyCreatedContext = new uctx; + uint8_t* stackPointer = new uint8_t[STACK_SIZE]; + static_cast(newlyCreatedContext)->uc_stack.ss_sp = stackPointer; + static_cast(newlyCreatedContext)->uc_stack.ss_size = STACK_SIZE; + + ContextMakingData makingData{ newlyCreatedContext, this}; + makecontext(static_cast(newlyCreatedContext), reinterpret_cast(contextProcedureStatic), reinterpret_cast(&makingData)); + + uctx* oldContext = static_cast(currentContext->uctx); + if (swapcontext(oldContext, newlyCreatedContext) == -1) { + throw std::runtime_error("Dispatcher::getReusableContext, swapcontext failed, " + lastErrorMessage()); + } + + assert(firstReusableContext != nullptr); + assert(firstReusableContext->uctx == newlyCreatedContext); + firstReusableContext->stackPtr = stackPointer; + } + + NativeContext* context = firstReusableContext; + firstReusableContext = firstReusableContext->next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + +int Dispatcher::getTimer() { + int timer; + if (timers.empty()) { + timer = ++lastCreatedTimer; + } else { + timer = timers.top(); + timers.pop(); + } + + return timer; +} + +void Dispatcher::pushTimer(int timer) { + timers.push(timer); +} + +void Dispatcher::contextProcedure(void* ucontext) { + assert(firstReusableContext == nullptr); + NativeContext context; + context.uctx = ucontext; + context.interrupted = false; + context.next = nullptr; + context.inExecutionQueue = false; + firstReusableContext = &context; + uctx* oldContext = static_cast(context.uctx); + if (swapcontext(oldContext, static_cast(currentContext->uctx)) == -1) { + throw std::runtime_error("Dispatcher::contextProcedure, swapcontext failed, " + lastErrorMessage()); + } + + for (;;) { + ++runningContextCount; + try { + context.procedure(); + } catch(std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + + dispatch(); + } +} + +void Dispatcher::contextProcedureStatic(intptr_t context) { + ContextMakingData* makingContextData = reinterpret_cast(context); + makingContextData->dispatcher->contextProcedure(makingContextData->uctx); +} + +} diff --git a/src/Platform/Haiku/System/Dispatcher.h b/src/Platform/Haiku/System/Dispatcher.h new file mode 100644 index 0000000..4a07283 --- /dev/null +++ b/src/Platform/Haiku/System/Dispatcher.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +namespace System { + +struct NativeContextGroup; + +struct NativeContext { + void* uctx; + void* stackPtr{nullptr}; + bool interrupted; + bool inExecutionQueue; + NativeContext* next{nullptr}; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + +struct OperationContext { + NativeContext* context; + bool interrupted; +}; + +class Dispatcher { +public: + Dispatcher(); + Dispatcher(const Dispatcher&) = delete; + ~Dispatcher(); + Dispatcher& operator=(const Dispatcher&) = delete; + void clear(); + void dispatch(); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); + void remoteSpawn(std::function&& procedure); + void yield(); + + int getKqueue() const; + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); + int getTimer(); + void pushTimer(int timer); + +#ifdef __LP64__ + static const int SIZEOF_PTHREAD_MUTEX_T = 56 + sizeof(long); +#else + static const int SIZEOF_PTHREAD_MUTEX_T = 40 + sizeof(long); +#endif + +private: + void spawn(std::function&& procedure); + + int kqueue; + int lastCreatedTimer; + alignas(std::max_align_t) uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + std::atomic remoteSpawned; + std::queue> remoteSpawningProcedures; + std::stack timers; + + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; + + void contextProcedure(void* uctx); + static void contextProcedureStatic(intptr_t context); +}; + +} diff --git a/src/Platform/Haiku/System/ErrorMessage.cpp b/src/Platform/Haiku/System/ErrorMessage.cpp new file mode 100644 index 0000000..3eac41a --- /dev/null +++ b/src/Platform/Haiku/System/ErrorMessage.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "ErrorMessage.h" +#include +#include + +namespace System { + +std::string lastErrorMessage() { + return errorMessage(errno); +} + +std::string errorMessage(int err) { + return "result=" + std::to_string(err) + ", " + std::strerror(err); +} + +} diff --git a/src/Platform/Haiku/System/ErrorMessage.h b/src/Platform/Haiku/System/ErrorMessage.h new file mode 100644 index 0000000..13d3f63 --- /dev/null +++ b/src/Platform/Haiku/System/ErrorMessage.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include + +namespace System { +std::string lastErrorMessage(); +std::string errorMessage(int); +} diff --git a/src/Platform/Haiku/System/Future.h b/src/Platform/Haiku/System/Future.h new file mode 100644 index 0000000..cda1165 --- /dev/null +++ b/src/Platform/Haiku/System/Future.h @@ -0,0 +1,171 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2022, The Talleo developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +namespace Detail { + +namespace { + +enum class State : unsigned { + STARTED, + COMPLETED, + CONSUMED +}; + +} + +// Simplest possible future implementation. The reason why this class even exist is because currenty std future has a +// memory corrupting bug on OSX. Spawn a new thread, execute procedure in it, get result, and wait thread to shut down. +// Actualy, this is what libstdc++'s std::future is doing. +template class Future { +public: + // Create a new thread, and run `operation` in it. + explicit Future(std::function&& operation) : procedure(std::move(operation)), state(State::STARTED), worker{[this] { asyncOp(); }} { + } + + // Wait for async op to complete, then if thread wasn't detached, join it. + ~Future() { + wait(); + if (worker.joinable()) { + worker.join(); + } + } + + // Get result of async operation. UB if called more than once. + T get() const { + assert(state != State::CONSUMED); + wait(); + state = State::CONSUMED; + if (currentException != nullptr) { + std::rethrow_exception(currentException); + } + + return std::move(result); + } + + // Wait for async operation to complete, if op is already completed, return immediately. + void wait() const { + std::unique_lock guard(operationMutex); + while (state == State::STARTED) { + operationCondition.wait(guard); + } + } + + bool valid() const { + std::unique_lock guard(operationMutex); + return state != State::CONSUMED; + } + +private: + // This function is executed in a separate thread. + void asyncOp() { + try { + assert(procedure != nullptr); + result = procedure(); + } catch (...) { + currentException = std::current_exception(); + } + + std::unique_lock guard(operationMutex); + state = State::COMPLETED; + operationCondition.notify_one(); + } + + mutable T result; + std::function procedure; + std::exception_ptr currentException; + mutable std::mutex operationMutex; + mutable std::condition_variable operationCondition; + mutable State state; + std::thread worker; +}; + +template<> class Future { +public: + // Create a new thread, and run `operation` in it. + explicit Future(std::function&& operation) : procedure(std::move(operation)), state(State::STARTED), worker{[this] { asyncOp(); }} { + } + + // Wait for async op to complete, then if thread wasn't detached, join it. + ~Future() { + wait(); + if (worker.joinable()) { + worker.join(); + } + } + + // Get result of async operation. UB if called more than once. + void get() const { + assert(state != State::CONSUMED); + wait(); + state = State::CONSUMED; + if (currentException != nullptr) { + std::rethrow_exception(currentException); + } + } + + // Wait for async operation to complete, if op is already completed, return immediately. + void wait() const { + std::unique_lock guard(operationMutex); + while (state == State::STARTED) { + operationCondition.wait(guard); + } + } + + bool valid() const { + std::unique_lock guard(operationMutex); + return state != State::CONSUMED; + } + +private: + // This function is executed in a separate thread. + void asyncOp() { + try { + assert(procedure != nullptr); + procedure(); + } catch (...) { + currentException = std::current_exception(); + } + + std::unique_lock guard(operationMutex); + state = State::COMPLETED; + operationCondition.notify_one(); + } + + std::function procedure; + std::exception_ptr currentException; + mutable std::mutex operationMutex; + mutable std::condition_variable operationCondition; + mutable State state; + std::thread worker; +}; + +template std::function async(std::function&& operation) { + return std::move(operation); +} + +} + +} diff --git a/src/Platform/Haiku/System/Ipv4Resolver.cpp b/src/Platform/Haiku/System/Ipv4Resolver.cpp new file mode 100644 index 0000000..6a9ddeb --- /dev/null +++ b/src/Platform/Haiku/System/Ipv4Resolver.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "Ipv4Resolver.h" +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace System { + +Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { +} + +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { +} + +Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + other.dispatcher = nullptr; + } +} + +Ipv4Resolver::~Ipv4Resolver() { +} + +Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + other.dispatcher = nullptr; + } + + return *this; +} + +Ipv4Address Ipv4Resolver::resolve(const std::string& host) { + assert(dispatcher != nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo* addressInfos; + int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos); + if (result != 0) { + throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, " + errorMessage(result)); + } + + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::mt19937 generator{ std::random_device()() }; + std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + Ipv4Address address(ntohl(reinterpret_cast(addressInfo->ai_addr)->sin_addr.s_addr)); + freeaddrinfo(addressInfo); + return address; +} + +} diff --git a/src/Platform/Haiku/System/Ipv4Resolver.h b/src/Platform/Haiku/System/Ipv4Resolver.h new file mode 100644 index 0000000..e893b7f --- /dev/null +++ b/src/Platform/Haiku/System/Ipv4Resolver.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; + +class Ipv4Resolver { +public: + Ipv4Resolver(); + explicit Ipv4Resolver(Dispatcher& dispatcher); + Ipv4Resolver(const Ipv4Resolver&) = delete; + Ipv4Resolver(Ipv4Resolver&& other); + ~Ipv4Resolver(); + Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; + Ipv4Resolver& operator=(Ipv4Resolver&& other); + Ipv4Address resolve(const std::string& host); + +private: + Dispatcher* dispatcher; +}; + +} diff --git a/src/Platform/Haiku/System/TcpConnection.cpp b/src/Platform/Haiku/System/TcpConnection.cpp new file mode 100644 index 0000000..48de021 --- /dev/null +++ b/src/Platform/Haiku/System/TcpConnection.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2022-2024, The Talleo developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "TcpConnection.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Dispatcher.h" +#include +#include +#include + +namespace System { + +TcpConnection::TcpConnection() : dispatcher(nullptr) { +} + +TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); + connection = other.connection; + readContext = nullptr; + writeContext = nullptr; + other.dispatcher = nullptr; + } +} + +TcpConnection::~TcpConnection() { + if (dispatcher != nullptr) { + assert(readContext == nullptr); + assert(writeContext == nullptr); + int result = close(connection); + assert(result != -1); + } +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& other) { + if (dispatcher != nullptr) { + assert(readContext == nullptr); + assert(writeContext == nullptr); + if (close(connection) == -1) { + throw std::runtime_error("TcpConnection::operator=, close failed, " + lastErrorMessage()); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); + connection = other.connection; + readContext = nullptr; + writeContext = nullptr; + other.dispatcher = nullptr; + } + + return *this; +} + +size_t TcpConnection::read(uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(readContext == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + std::string message; + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + message = "recv failed, " + lastErrorMessage(); + } else { + OperationContext context; + context.context = dispatcher->getCurrentContext(); + context.interrupted = false; + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + readContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(readContext != nullptr); + OperationContext* context = static_cast(readContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::interruptionProcedure, kevent failed, " + lastErrorMessage()); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(dispatcher != nullptr); + assert(context.context == dispatcher->getCurrentContext()); + assert(readContext == &context); + readContext = nullptr; + context.context = nullptr; + if (context.interrupted) { + throw InterruptedException(); + } + + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + message = "recv failed, " + lastErrorMessage(); + } else { + assert(transferred <= static_cast(size)); + return transferred; + } + } + } + + throw std::runtime_error("TcpConnection::read, " + message); + } + + assert(transferred <= static_cast(size)); + return transferred; +} + +size_t TcpConnection::write(const uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(writeContext == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + std::string message; + if (size == 0) { + if (shutdown(connection, SHUT_WR) == -1) { + throw std::runtime_error("TcpConnection::write, shutdown failed, " + lastErrorMessage()); + } + + return 0; + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + message = "send failed, " + lastErrorMessage(); + } else { + OperationContext context; + context.context = dispatcher->getCurrentContext(); + context.interrupted = false; + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, &context); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + writeContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(writeContext != nullptr); + OperationContext* context = static_cast(writeContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent failed, " + lastErrorMessage()); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(dispatcher != nullptr); + assert(context.context == dispatcher->getCurrentContext()); + assert(writeContext == &context); + writeContext = nullptr; + context.context = nullptr; + if (context.interrupted) { + throw InterruptedException(); + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + message = "send failed, " + lastErrorMessage(); + } else { + assert(transferred <= static_cast(size)); + return transferred; + } + } + } + + throw std::runtime_error("TcpConnection::write, " + message); + } + + assert(transferred <= static_cast(size)); + return transferred; +} + +std::pair TcpConnection::getPeerAddressAndPort() const { + sockaddr_in addr; + socklen_t size = sizeof(addr); + if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, " + lastErrorMessage()); + } + + assert(size == sizeof(sockaddr_in)); + return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), readContext(nullptr), writeContext(nullptr) { + int val = 1; + if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { + throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, " + lastErrorMessage()); + } +} + +} diff --git a/src/Platform/Haiku/System/TcpConnection.h b/src/Platform/Haiku/System/TcpConnection.h new file mode 100644 index 0000000..5ed4d8d --- /dev/null +++ b/src/Platform/Haiku/System/TcpConnection.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; + +class TcpConnection { +public: + TcpConnection(); + TcpConnection(const TcpConnection&) = delete; + TcpConnection(TcpConnection&& other); + ~TcpConnection(); + TcpConnection& operator=(const TcpConnection&) = delete; + TcpConnection& operator=(TcpConnection&& other); + std::size_t read(uint8_t* data, std::size_t size); + std::size_t write(const uint8_t* data, std::size_t size); + std::pair getPeerAddressAndPort() const; + +private: + friend class TcpConnector; + friend class TcpListener; + + Dispatcher* dispatcher; + int connection; + void* readContext; + void* writeContext; + + TcpConnection(Dispatcher& dispatcher, int socket); +}; + +} diff --git a/src/Platform/Haiku/System/TcpConnector.cpp b/src/Platform/Haiku/System/TcpConnector.cpp new file mode 100644 index 0000000..b053ae7 --- /dev/null +++ b/src/Platform/Haiku/System/TcpConnector.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2022-2024, The Talleo developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "TcpConnector.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "Dispatcher.h" +#include "ErrorMessage.h" +#include "TcpConnection.h" + +namespace System { + +namespace { + +struct ConnectorContext : public OperationContext { + int connection; +}; + +} + +TcpConnector::TcpConnector() : dispatcher(nullptr) { +} + +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { +} + +TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + context = nullptr; + other.dispatcher = nullptr; + } +} + +TcpConnector::~TcpConnector() { + assert(dispatcher == nullptr || context == nullptr); +} + +TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + context = nullptr; + other.dispatcher = nullptr; + } + + return *this; +} + +TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + std::string message; + int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == -1) { + message = "socket failed, " + lastErrorMessage(); + } else { + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + message = "bind failed, " + lastErrorMessage(); + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl failed, " + lastErrorMessage(); + } else { + sockaddr_in addressData; + addressData.sin_family = AF_INET; + addressData.sin_port = htons(port); + addressData.sin_addr.s_addr = htonl(address.getValue()); + int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); + if (result == -1) { + if (errno == EINPROGRESS) { + ConnectorContext connectorContext; + connectorContext.context = dispatcher->getCurrentContext(); + connectorContext.interrupted = false; + connectorContext.connection = connection; + + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, &connectorContext); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + context = &connectorContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + ConnectorContext* connectorContext = static_cast(context); + if (!connectorContext->interrupted) { + if (close(connectorContext->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, " + lastErrorMessage()); + } + + dispatcher->pushContext(connectorContext->context); + connectorContext->interrupted = true; + } + }; + + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(dispatcher != nullptr); + assert(connectorContext.context == dispatcher->getCurrentContext()); + assert(context == &connectorContext); + context = nullptr; + connectorContext.context = nullptr; + if (connectorContext.interrupted) { + throw InterruptedException(); + } + + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + message = "getsockopt failed, " + lastErrorMessage(); + } else { + if (retval != 0) { + message = "getsockopt failed, " + lastErrorMessage(); + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + } + } + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + + int result = close(connection); + assert(result != -1);; + } + + throw std::runtime_error("TcpConnector::connect, " + message); +} + +} diff --git a/src/Platform/Haiku/System/TcpConnector.h b/src/Platform/Haiku/System/TcpConnector.h new file mode 100644 index 0000000..02f3129 --- /dev/null +++ b/src/Platform/Haiku/System/TcpConnector.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; +class TcpConnection; + +class TcpConnector { +public: + TcpConnector(); + TcpConnector(Dispatcher& dispatcher); + TcpConnector(const TcpConnector&) = delete; + TcpConnector(TcpConnector&& other); + ~TcpConnector(); + TcpConnector& operator=(const TcpConnector&) = delete; + TcpConnector& operator=(TcpConnector&& other); + TcpConnection connect(const Ipv4Address& address, uint16_t port); + +private: + void* context; + Dispatcher* dispatcher; +}; + +} diff --git a/src/Platform/Haiku/System/TcpListener.cpp b/src/Platform/Haiku/System/TcpListener.cpp new file mode 100644 index 0000000..278c8d4 --- /dev/null +++ b/src/Platform/Haiku/System/TcpListener.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "TcpListener.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Dispatcher.h" +#include "TcpConnection.h" +#include +#include +#include + +namespace System { + +TcpListener::TcpListener() : dispatcher(nullptr) { +} + +TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16_t port) : dispatcher(&dispatcher) { + std::string message; + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + message = "socket failed, " + lastErrorMessage(); + } else { + int flags = fcntl(listener, F_GETFL, 0); + if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) { + message = "fcntl failed, " + lastErrorMessage(); + } else { + int on = 1; + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) { + message = "setsockopt failed, " + lastErrorMessage(); + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = htonl(addr.getValue()); + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + message = "bind failed, " + lastErrorMessage(); + } else if (listen(listener, SOMAXCONN) != 0) { + message = "listen failed, " + lastErrorMessage(); + } else { + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE | EV_CLEAR, 0, SOMAXCONN, NULL); + + if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + context = nullptr; + return; + } + } + } + } + + if (close(listener) == -1) { + message = "close failed, " + lastErrorMessage(); + } + } + + throw std::runtime_error("TcpListener::TcpListener, " + message); +} + +TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + listener = other.listener; + context = nullptr; + other.dispatcher = nullptr; + } +} + +TcpListener::~TcpListener() { + if (dispatcher != nullptr) { + assert(context == nullptr); + int result = close(listener); + assert(result != -1); + } +} + +TcpListener& TcpListener::operator=(TcpListener&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(listener) == -1) { + throw std::runtime_error("TcpListener::operator=, close failed, " + lastErrorMessage()); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + listener = other.listener; + context = nullptr; + other.dispatcher = nullptr; + } + + return *this; +} + +TcpConnection TcpListener::accept() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + std::string message; + OperationContext listenerContext; + listenerContext.context = dispatcher->getCurrentContext(); + listenerContext.interrupted = false; + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR , 0, SOMAXCONN, &listenerContext); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent failed, " + lastErrorMessage(); + } else { + context = &listenerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent failed, " + lastErrorMessage()); + } + + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); + } + }; + + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(dispatcher != nullptr); + assert(listenerContext.context == dispatcher->getCurrentContext()); + assert(context == &listenerContext); + context = nullptr; + listenerContext.context = nullptr; + if (listenerContext.interrupted) { + throw InterruptedException(); + } + + sockaddr inAddr; + socklen_t inLen = sizeof(inAddr); + int connection = ::accept(listener, &inAddr, &inLen); + if (connection == -1) { + message = "accept failed, " + lastErrorMessage(); + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl failed, " + lastErrorMessage(); + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + + throw std::runtime_error("TcpListener::accept, " + message); +} + +} diff --git a/src/Platform/Haiku/System/TcpListener.h b/src/Platform/Haiku/System/TcpListener.h new file mode 100644 index 0000000..9cbb843 --- /dev/null +++ b/src/Platform/Haiku/System/TcpListener.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; +class TcpConnection; + +class TcpListener { +public: + TcpListener(); + TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uint16_t port); + TcpListener(const TcpListener&) = delete; + TcpListener(TcpListener&& other); + ~TcpListener(); + TcpListener& operator=(const TcpListener&) = delete; + TcpListener& operator=(TcpListener&& other); + TcpConnection accept(); + +private: + Dispatcher* dispatcher; + int listener; + void* context; +}; + +} diff --git a/src/Platform/Haiku/System/Timer.cpp b/src/Platform/Haiku/System/Timer.cpp new file mode 100644 index 0000000..137827f --- /dev/null +++ b/src/Platform/Haiku/System/Timer.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#include "Timer.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Dispatcher.h" +#include +#include + +namespace System { + +Timer::Timer() : dispatcher(nullptr) { +} + +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), timer(-1) { +} + +Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + timer = other.timer; + context = nullptr; + other.dispatcher = nullptr; + } +} + +Timer::~Timer() { + assert(dispatcher == nullptr || context == nullptr); +} + +Timer& Timer::operator=(Timer&& other) { + assert(dispatcher == nullptr || context == nullptr); + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + assert(other.context == nullptr); + timer = other.timer; + context = nullptr; + other.dispatcher = nullptr; + other.timer = -1; + } + + return *this; +} + +void Timer::sleep(std::chrono::nanoseconds duration) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + + OperationContext timerContext; + timerContext.context = dispatcher->getCurrentContext(); + timerContext.interrupted = false; + timer = dispatcher->getTimer(); + + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_NSECONDS, duration.count(), &timerContext); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Timer::stop, kevent failed, " + lastErrorMessage()); + } + + context = &timerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Timer::stop, kevent failed, " + lastErrorMessage()); + } + + dispatcher->pushContext(timerContext->context); + timerContext->interrupted = true; + } + }; + + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(dispatcher != nullptr); + assert(timerContext.context == dispatcher->getCurrentContext()); + assert(context == &timerContext); + context = nullptr; + timerContext.context = nullptr; + dispatcher->pushTimer(timer); + if (timerContext.interrupted) { + throw InterruptedException(); + } +} + +} diff --git a/src/Platform/Haiku/System/Timer.h b/src/Platform/Haiku/System/Timer.h new file mode 100644 index 0000000..023d895 --- /dev/null +++ b/src/Platform/Haiku/System/Timer.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Karbo. +// +// Karbo 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 3 of the License, or +// (at your option) any later version. +// +// Karbo 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 Karbo. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; + +class Timer { +public: + Timer(); + explicit Timer(Dispatcher& dispatcher); + Timer(const Timer&) = delete; + Timer(Timer&& other); + ~Timer(); + Timer& operator=(const Timer&) = delete; + Timer& operator=(Timer&& other); + void sleep(std::chrono::nanoseconds duration); + +private: + Dispatcher* dispatcher; + int timer; + void* context; +}; + +} diff --git a/src/Platform/Haiku/System/asm.s b/src/Platform/Haiku/System/asm.s new file mode 100644 index 0000000..1df0332 --- /dev/null +++ b/src/Platform/Haiku/System/asm.s @@ -0,0 +1,47 @@ +.globl _setmcontext +_setmcontext: + movq 16(%rdi), %rsi + movq 24(%rdi), %rdx + movq 32(%rdi), %rcx + movq 40(%rdi), %r8 + movq 48(%rdi), %r9 + movq 56(%rdi), %rax + movq 64(%rdi), %rbx + movq 72(%rdi), %rbp + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + movq 184(%rdi), %rsp + pushq 160(%rdi) /* new %eip */ + movq 8(%rdi), %rdi + ret + +.globl _getmcontext +_getmcontext: + movq %rdi, 8(%rdi) + movq %rsi, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rcx, 32(%rdi) + movq %r8, 40(%rdi) + movq %r9, 48(%rdi) + movq $1, 56(%rdi) /* %rax */ + movq %rbx, 64(%rdi) + movq %rbp, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + movq (%rsp), %rcx /* %rip */ + movq %rcx, 160(%rdi) + leaq 8(%rsp), %rcx /* %rsp */ + movq %rcx, 184(%rdi) + + movq 32(%rdi), %rcx /* restore %rcx */ + movq $0, %rax + ret \ No newline at end of file diff --git a/src/crypto/slow-hash-x86.c b/src/crypto/slow-hash-x86.c index 460114e..1e089f6 100644 --- a/src/crypto/slow-hash-x86.c +++ b/src/crypto/slow-hash-x86.c @@ -2,6 +2,7 @@ // Copyright (c) 2014-2018, The Monero Project // Copyright (c) 2014-2018, The Aeon Project // Copyright (c) 2018-2019, The TurtleCoin Developers +// Copyright (c) 2024, The Talleo developers // // Please see the included LICENSE file for more information. @@ -424,7 +425,7 @@ void slow_hash_allocate_state(uint32_t page_size) SetLockPagesPrivilege(GetCurrentProcess(), TRUE); hp_state = (uint8_t *)VirtualAlloc(hp_state, page_size, MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #else -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__HAIKU__) hp_state = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); #else hp_state = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0);